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] 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 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 +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 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 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 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 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 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�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 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 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⪙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 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%0AWr{YFF+{1eDhN(&%*h%u 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 #KAxLt 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

WqB+4Ue}Rq>7H-m7pvOvI7a$%k443$sYjon9bdj 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 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