diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6e22d1c0f..36bac0018 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,12 +112,12 @@ jobs: with: result-encoding: string script: | - const tagRefObj = await github.git.getRef({ + const tagRefObj = await github.rest.git.getRef({ ...context.repo, ref: context.ref.replace('refs/', '') }); - const tagObj = await github.git.getTag({ + const tagObj = await github.rest.git.getTag({ ...context.repo, tag_sha: tagRefObj.data.object.sha }); diff --git a/.gitignore b/.gitignore index 70cc10dd0..05796194e 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ dist/Espressif_flash_download_tool_v3.8.5/dl_temp/bin_tmp/ dist/Espressif_flash_download_tool_v3.8.5/dl_temp/_temp_by_dltool/ dist/Espressif_flash_download_tool_v3.8.5/configure/esp32/spi_download.conf +src/CustomIR.h diff --git a/docs/source/Plugin/AdaGFX_commands.repl b/docs/source/Plugin/AdaGFX_commands.repl index dd86b1a34..75e9ae72c 100644 --- a/docs/source/Plugin/AdaGFX_commands.repl +++ b/docs/source/Plugin/AdaGFX_commands.repl @@ -33,6 +33,7 @@ * 0 : Continue to next line (wrap text onto the next line) * 1 : Truncate exceeding message (cut-off text that won't fit on the screen width) * 2 : Clear then truncate exceeding message (Clear to width of screen, then print the message) + * 3 : Truncate and center message in available space, when maxTextWidthPixels is provided in ``txtfull`` subcommand. Will act like 1 : Truncate exceeding message if no width provided. " " ``,txt,`` @@ -69,11 +70,15 @@ Set font scaling option. Only values from 0 to 10 are accepted. 0 assumes 1 by default. " " - ``,txtfull,,,,,,`` + ``,txtfull,,,,,,,,`` "," Write text at position X/Y with all options. Depending on the setting **Text Coordinates in col/row**, these coordinates are pixels (default) or column/rows. All options that have a default specified (size, foreColor, backColor) can be left out, but text is expected. ForeColor and BackColor use the last set Foreground and Background colors (that will by default be white and transparent if not provided). + + ``textPrintMode`` can be passed an invalid value (f.e. -1) to use the current setting. + + ``maxTextWidthPixels`` can be used to have textPrintMode > 0 (see ``tpm`` subcommand) but clear only a limited area width instead of clearing until the right edge of the screen. 0 implies that the right edge of the screen is used. " " ``,asciitable[,,]`` diff --git a/docs/source/Plugin/NeoPixel_Power.rst b/docs/source/Plugin/NeoPixel_Power.rst new file mode 100644 index 000000000..2b4a09e08 --- /dev/null +++ b/docs/source/Plugin/NeoPixel_Power.rst @@ -0,0 +1,7 @@ +By the physical design of these modules, using NeoPixel leds for display that can consume up to 60 mA each, the power consumption is dependent on the number of neopixels that are activated and the brightness that is set. On a set of NeoPixels, like consisting of 64 pixels, the max. power used 60 mA * 64 = 3840 mA @ 5V = 19.2 watt with all leds on at max brightness (255), so it requires an adequate power supply, preferably separate from the power supply used to power the ESP. + +When reducing the brightness to 40% (0.4 * 255 = 102), that power is reduced to ca. 1.5 A @ 5V = ca. 7.5 watt. 40% is usually enough during daylight conditions, at night the brightness can often be reduced to 20%. + +For a set of 300 pixels, the max. power would count up to 300 * 60 mA @ 5V = 90 W. At 40% brightness, that is reduced to 36 W. + +Reasoning back from the available power supply max. load (f.e. 30 W), the max. allowed brightness can be calculated by dividing the max. load by the voltage (5V) and the number of pixels (300) = 20 mA, 60 / 20 = 3 = ~33% brightness = 85. That will still be bright enough, but will put quite a strain at the power supply. \ No newline at end of file diff --git a/docs/source/Plugin/P016.rst b/docs/source/Plugin/P016.rst index c4fe76c59..1a8df528b 100644 --- a/docs/source/Plugin/P016.rst +++ b/docs/source/Plugin/P016.rst @@ -44,14 +44,21 @@ Change log .. versionchanged:: 2.0 ... + 2022-08-08: + |added| - Major overhaul for 2.0 release. + The buffer used for receiving and processing the incoming data can now be manually configured. 2021-05-24: |added| Support for IR codes up to 64 bit with separated Decode type and Repeat option, existing settings are converted, reworked the settings UI to reflect those changes. + . + + |added| + Major overhaul for 2.0 release. + .. versionadded:: 1.0 ... diff --git a/docs/source/Plugin/P016_DeviceConfiguration.png b/docs/source/Plugin/P016_DeviceConfiguration.png new file mode 100644 index 000000000..a33895fc9 Binary files /dev/null and b/docs/source/Plugin/P016_DeviceConfiguration.png differ diff --git a/docs/source/Plugin/P016_Setup.png b/docs/source/Plugin/P016_Setup.png deleted file mode 100644 index 3e2ea0e1d..000000000 Binary files a/docs/source/Plugin/P016_Setup.png and /dev/null differ diff --git a/docs/source/Plugin/P016_TSOP4838.rst b/docs/source/Plugin/P016_TSOP4838.rst index 008fafe10..b0aa06010 100644 --- a/docs/source/Plugin/P016_TSOP4838.rst +++ b/docs/source/Plugin/P016_TSOP4838.rst @@ -19,7 +19,7 @@ A datasheet for the TSOP4838 receiver can be found here: http://www.vishay.com/d Setup ----- -.. image:: P016_Setup.png +.. image:: P016_DeviceConfiguration.png Task settings ~~~~~~~~~~~~~ @@ -31,71 +31,52 @@ Task settings Sensor ^^^^^^ -* **Sensor**: standard definition for a GPIO pin setting +* **Internal PullUp**: To enable the internal pull-up resistor of the ESP. Usually not needed for the TSOP4838 as it has a built-in pull-up mechanism. + +* **Inversed Logic**: Can be used to invert the signal if a different type of receiver is used. + +* **GPIO <- IR**: Select the pin that the TSOP is connected to. Avoid selecting a pin that has a warning sign next to it, as that may block the boot process or normal working of the ESP. + +* **Receiver buffer size**: For receiving and decoding the data from the sensor, a buffer is needed. For more complex protocols, like often used for Air Conditioner remote controls, a larger buffer may be needed. By default this buffer is dimensioned at 300 bytes (will be rounded up to the next even number when stored), and needs to be enlarged if a message of this intent is shown in the log at INFO level (Either Web or serial log). Content ^^^^^^^ -Add new received code to command lines -"""""""""""""""""""""""""""""""""""""" +* **Add new received code to command lines**: After receiving a valid IR code the code is automatically added to the following table if it not already exists. -After receiving a valid IR code the code is automatically added to the following table if it not already exists. +* **Execute commands**: After receiving a valid IR the command that is assigned to this code (**Code [Hex]** or **Alt. Code [Hex]**) is executed. -Execute commands -"""""""""""""""" +* **Inhibit time for the same command [ms]**: The time that a same command will be ignored if receiving it multiple times. When set too high, often repeated commands like volume-up or volume-down will respond slower than expected. -After receiving a valid IR the command that is assigned to this code (**Code [Hex]** or **Alt. Code [Hex]**) is executed. - -Inhibit time for the same command [ms] -"""""""""""""""""""""""""""""""""""""" - -The time that a same command will be ignored if receiving it multiple times. When set too high, often repeated commands like volume-up or volume-down will respond slower than expected. - -.. note:: The code is only added automatically to field **Code [Hex]**! +.. note:: The code is *only* added automatically to field **Code [Hex]**! Code - command map ^^^^^^^^^^^^^^^^^^ -Decode type -"""""""""""" +* **Decode type**: The type as detected by the receiver, the list contains all supported types. -The type as detected by the receiver, the list contains all supported types. +* **Repeat**: If a repeat code is detected, this is checked. -Repeat -"""""" +* **Code [Hex]**: If the received valid IR code matches this setting the **Command nn** is executed. -If a repeat code is detected, this is checked. +* **Alt. Decode type**: The type as detected by the receiver, the list contains all supported types. -Code [Hex] -""""""""""""" +* **Repeat**: If a repeat code is detected, this is checked. -If the received valid IR code matches this setting the **Command nn** is executed. +* **Alt. Code [Hex]**: If the received valid IR code matches this setting the **Command nn** is executed. -Alt. Decode type -"""""""""""""""" - -The type as detected by the receiver, the list contains all supported types. - -Repeat -"""""" - -If a repeat code is detected, this is checked. - -Alt. Code [Hex] -""""""""""""""" - -If the received valid IR code matches this setting the **Command nn** is executed. - -Command nn -"""""""""" - -If the received valid IR code matches the code in **Code [Hex]** or **Alt. Code [Hex]** this command is executed. +* **Command nn**:If the received valid IR code matches the code in **Code [Hex]** or **Alt. Code [Hex]** this command is executed. .. note:: Up to 10 commands can be set ! +.. note:: Using compile-time options, the **Code - command map** and related options can be disabled. + Data acquisition -^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~ + +This group of settings, **Single event with all values** settings are standard available configuration items. Values -^^^^^^ +~~~~~~ +The last received code is available in numeric for in the Value that's selected here. It can be used in rules and other template processing plugins like Display devices. diff --git a/docs/source/Plugin/P038.rst b/docs/source/Plugin/P038.rst index 1c6feb557..93e2e42ef 100644 --- a/docs/source/Plugin/P038.rst +++ b/docs/source/Plugin/P038.rst @@ -31,6 +31,11 @@ Supported hardware A few examples of NeoPixel LEDs and strips (Image sourced from Aliexpress) +Power consumption +~~~~~~~~~~~~~~~~~ + +.. include:: NeoPixel_Power.rst + Configuration ------------- diff --git a/docs/source/Plugin/P041.rst b/docs/source/Plugin/P041.rst index e0da2b59f..b6d609c75 100644 --- a/docs/source/Plugin/P041.rst +++ b/docs/source/Plugin/P041.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p04x.repl +.. include:: ../Plugin/_plugin_substitutions_p04x.repl .. _P041_page: |P041_typename| @@ -26,6 +26,11 @@ Supported hardware |P041_usedby| +Power consumption +~~~~~~~~~~~~~~~~~ + +.. include:: NeoPixel_Power.rst + .. Commands available .. ^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/Plugin/P042.rst b/docs/source/Plugin/P042.rst index 034e986c4..076a183dc 100644 --- a/docs/source/Plugin/P042.rst +++ b/docs/source/Plugin/P042.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p04x.repl +.. include:: ../Plugin/_plugin_substitutions_p04x.repl .. _P042_page: |P042_typename| @@ -26,6 +26,11 @@ Supported hardware |P042_usedby| +Power consumption +~~~~~~~~~~~~~~~~~ + +.. include:: NeoPixel_Power.rst + .. Commands available .. ^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/Plugin/P070.rst b/docs/source/Plugin/P070.rst index d3858cbce..1399debc2 100644 --- a/docs/source/Plugin/P070.rst +++ b/docs/source/Plugin/P070.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p07x.repl +.. include:: ../Plugin/_plugin_substitutions_p07x.repl .. _P070_page: |P070_typename| @@ -26,6 +26,11 @@ Supported hardware |P070_usedby| +Power consumption +~~~~~~~~~~~~~~~~~ + +.. include:: NeoPixel_Power.rst + .. Commands available .. ^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/Plugin/P095.rst b/docs/source/Plugin/P095.rst index 4a1c5644e..b11b6d2db 100644 --- a/docs/source/Plugin/P095.rst +++ b/docs/source/Plugin/P095.rst @@ -24,21 +24,170 @@ Used libraries: |P095_usedlibraries| Description ----------- -This plugin allow to control a TFT screen (ILI9341) through HTTP API +The ILI9341, ILI9342, ILI9481, ILI9486 and ILI9488 chip families drive color TFT displays in a few resolutions, and support 65535 colors (RGB565 coded). -## Environment -Tested with WEMOS D1 Mini Pro and Wemos TDFT 2.4 -Tested with ESPEasy 2.4.2 -tag mega-201902225) +This plugin supports these display models: + +* **ILI934x** with resolution 240 x 320 pixels +* **ILI948x** with resolution 320 x 480 pixels + +The plugin can be configured to show predefined text, a reservation for up to 24 lines is made, but only the lines that can fit on-screen will be shown, or via commands, sent via http or from rules, text or graphical elements like lines, rectangles, circles, pixels, etc., can be shown. + +Hardware +-------- TFT Shield : https://docs.wemos.cc/en/latest/d1_mini_shiled/tft_2_4.html Price : ~ 5.40€/$ (https://fr.aliexpress.com/item/32919729730.html) +Connections +^^^^^^^^^^^ + +These displays are connected using the SPI interface, minimally with 3 signal wires, though usually also support the ``CS`` signal, so 4-wire SPI can be used, to allow multiple SPI-connected devices. + +The text on most displays is somewhat confusing, as not the usual SPI names are used: + +.. code-block:: none + + ESP TFT + -------- ------ + 3V3 --- VCC (most displays only support 3.3V) + GND --- GND + MOSI --> SDA/SDI + CLK --> SCL/SCK + (gpio) --> DC + (gpio) --> CS + (gpio) --> RES/RST/RESET (optional, Reset) + (gpio) --> BLK/LED (optional, Backlight, TFT-pin can also be connected to VCC for always-on) + + (gpio) = configurable GPIO pin. -Supported hardware ------------------- +Device configuration +-------------------- -|P095_usedby| +.. image:: P095_DeviceConfiguration.png + :alt: Device configuration + +* **Name** A unique name should be entered here. + +* **Enabled** The device can be disabled or enabled. When not enabled the device should not use any resources. + +Actuator +^^^^^^^^ + +* **GPIO -> TFT CS** Select the GPIO pin to use for the ``CS`` connection. If the display doesn't have a ``CS`` connection it can be set to *None*. +* **GPIO -> TFT DC** The GPIO pin to use for the ``DC`` connection (Data/Command). +* **GPIO -> TFT RST** Select the GPIO pin to use for the ``RES`` (reset) connection. If the display doesn't have a ``RES`` (or RST) connection, or no free pin is available, it can be set to *None*. If it is set to None, for proper operation it may need too be wired to the Reset connection on the ESP, so the device is initialized correctly. +* **GPIO -> Backlight (optional)** Select the GPIO pin to use for controlling the backlight. To save power, the backlight can be dimmed, or turned off if the display is turned off. If set to *None*, usually the max. brightness is used for the backlight. +* **Backlight percentage** The backlight can be controlled via PWM modulation on the Backlight (BLK) pin of the display. This is set as a percentage between 1 and 100. +* **Display button** A GPIO pin can be configured to wake the display on demand. This, combined with the **Display Timeout** setting, can preserve the lifetime of the display, and save some power. +* **Inversed Logic** When checked, reverses the pin-state action of the **Display button** gpio. This allows an external circuit, f.e. an IR sensor, that may provide a *high* signal when activated, to wake the display. +* **Display Timeout** Select the timeout in seconds to turn off the display after the last update or wake-up. Only used if the **Display button** is *also* configured. +* **TFT Display model** Select the hardware model that is connected. Currently there are only preset resolutions available. + +Available options: + +.. image:: P095_TFTDisplayModelOptions.png + :alt: TFT Display model options + +.. warning:: + The **ILI9481** display controller does have issues when rotating, by using the ``,rot,`` command, to change the display orientation after some content is already displayed on the screen (content may move, rotate and/or mirror unexpectedly). It is advised to clear the screen after changing the rotation setting. + +Layout +^^^^^^^^ + +* **Rotation** Depending on how the display is to be mounted/installed, it may be needed to rotate the content, or with a non-square resolution, to use the display in *Landscape* layout instead of the default *Portrait*. + +Available options: + +.. image:: P095_RotationOptions.png + :alt: Rotation + +The available options rotate the display in a clock-wise direction. + +* **Text print Mode** Defines the handling of text when predefined text or text via the ``txt`` or ``txtfull`` subcommands (see below) is displayed. + +Available options: + +.. image:: P095_TextPrintModeOptions.png + :alt: Text print mode + +* *Continue to next line* Simply 'prints' all text to the display, with automatic overflow to the next line(s) if the text doesn't fit on the current line. +* *Truncate exceeding message* Display the provided message, but any excess at the right border of the display is discarded. +* *Clear then truncate exceeding message* Clear from the current cursor position to the end of the display, then display the provided message, but any excess at the right border of the display is discarded. +* *Truncate, centered if maxWidth set* Like *Truncate exceeding message* but centered in available width when that is provided (in ``txtfull`` subcommand). + +Default setting is *Continue to next line*. + +* **Font scaling** The scaling factor for the currently active font. Select a factor between 1 and 10. + +* **Show splash on start** When available, and enabled, will show an ESPEasy & plugin name text-splash during start of the plugin. If also some content is configured, this will be written over the splash, as that is not cleared after being displayed. + +* **Clear display on exit** When checked, will clear the display when the task is disabled, either from settings or via the ``TaskDisable`` command. The screen will be turned off, and when a backlight pin is configured, also the backlight is turned off. + +* **Write Command trigger** The command to handle any commands for this device can be selected here. This can make the commands compatible with other (tft) displays, using the same command structure via the ESPEasy Adafruit Graphics helper class. + +Available options: + +.. image:: P095_WriteCommandTriggerOptions.png + :alt: Write-command trigger + +* *tft* +* *ili9341* +* *ili9342* +* *ili9481* +* *ili9486* +* *ili9488* + +The command is handled non-case sensitive. See below for available commands and subcommands. + +* **Wake display on receiving text** When checked, the display will be enabled once any content is written to the screen, either triggered by the Interval, or from a command. Default checked. + +* **Text Coordinates in col/row** When checked, the coordinates for the ``txp``, ``txz`` and ``txtfull`` subcommands will be handled in cursor columns & rows, instead of pixels. Column and row are calculated from the current font size and font scaling settings. + +* **Use -1px offset for txp & txtfull** For backward compatibility with the previous, non-AdafruitGFX_helper based, plugin implementation, that used 0-based coordinates, an offset of -1 pixel can be applied to the ``txp``, ``txz`` and ``txtfull`` subcommands. This option is enabled by default. + +* **Background-fill for text** When checked, for any text-line sent to the screen, the entire background (including top and bottom lines) will have the provided background color, *unless* transparent is used (Background color == Foreground color). Default checked. + +Content +^^^^^^^^ + +* **Foreground color** +* **Background color** + +These are the default colors, used to display the content as configured below (if any). The background color is also used as the Clear screen color. + +Colors can be specified in 3 ways: + +* *Color name* Some named colors are recognized: White, Black, Red, Green, Blue, Yellow, Orange, Navy, Darkgreen, Cyan, Darkcyan, Maroon, Purple, Olive, Magenta, Lightgrey, Darkgrey, Pink and Greenyellow. If any of these colors is recognized, the name will be shown. +* *#RGB565* A ``#`` prefixed, 1 to 4 digit, hex-coded color value in RGB565 format (5 bits red, 6 bits green, 5 bits blue) giving the max. number of 65535 colors. This is the value that is shown after the settings are saved, and it is not one of the known color names. +* *#RRGGBB* A ``#`` prefixed, 6 digit, hex coded RGB color value. Will be transformed to an RGB565 value! + +If the Foreground and Background colors are the same, the background color will become ``transparent``. If the Forground color is empty, as a default ``white`` will be set. + +* **Line 1..24** Predefined content can be specified. The number of lines available depends on the size of the display, the font used, the font scaling that is set and the selected rotation. + +The usual variables, like ``[Taskname#Valuename]``, or ``%v1%``, system variables, formulas and functions can be used. + +Input length is limited to 60 characters per line. If a longer calculated text is needed for a line, then it should be set from the rules or from an external source via http commands. + +Next to Line 24, the remaining capacity in characters is displayed. + +The total combination of lines * input length can not exceed 1000 characters (sized dynamically), as there is limited storage per task available for these settings. An error message will be shown after (trying to) save the settings, **any excess content will be discarded!** + +Example: + +.. image:: P095_SaveError.png + :alt: Save error + +* **Interval** By default, Interval will be set to 0. If set to a non-zero value, the pre-configured content will be updated automatically using that interval (seconds). Depending on the **Text print Mode** setting, content that may have been draw from rules or external commands, may be erased. + +Values +^^^^^^ + +The current text-cursor position is available in 2 variables **CursorX** and **CursorY**, that will be updated on every change of text or received command. **No** events are generated, and these values will never be sent to controllers. + +The values can be used in rules, if desired. They will follow the **Text Coordinates in col/row** setting. Commands available ^^^^^^^^^^^^^^^^^^ @@ -54,4 +203,6 @@ Change log .. versionchanged:: 2.0 ... - |added| 2020-04-20 + |added| 2020-04-20 Initially added + + |added| 2022-04-23 Rewrite of the plugin based on AdafruitGFX_helper, udated documentation based on shared settings with :ref:`P116_page` and AdafruitGFX_helper diff --git a/docs/source/Plugin/P095_DeviceConfiguration.png b/docs/source/Plugin/P095_DeviceConfiguration.png new file mode 100644 index 000000000..c00a7a108 Binary files /dev/null and b/docs/source/Plugin/P095_DeviceConfiguration.png differ diff --git a/docs/source/Plugin/P095_RotationOptions.png b/docs/source/Plugin/P095_RotationOptions.png new file mode 100644 index 000000000..ca110533b Binary files /dev/null and b/docs/source/Plugin/P095_RotationOptions.png differ diff --git a/docs/source/Plugin/P095_SaveError.png b/docs/source/Plugin/P095_SaveError.png new file mode 100644 index 000000000..f17023400 Binary files /dev/null and b/docs/source/Plugin/P095_SaveError.png differ diff --git a/docs/source/Plugin/P095_TFTDisplayModelOptions.png b/docs/source/Plugin/P095_TFTDisplayModelOptions.png new file mode 100644 index 000000000..e574d54ea Binary files /dev/null and b/docs/source/Plugin/P095_TFTDisplayModelOptions.png differ diff --git a/docs/source/Plugin/P095_TextPrintModeOptions.png b/docs/source/Plugin/P095_TextPrintModeOptions.png new file mode 100644 index 000000000..11f537881 Binary files /dev/null and b/docs/source/Plugin/P095_TextPrintModeOptions.png differ diff --git a/docs/source/Plugin/P095_WriteCommandTriggerOptions.png b/docs/source/Plugin/P095_WriteCommandTriggerOptions.png new file mode 100644 index 000000000..bbd9e9a49 Binary files /dev/null and b/docs/source/Plugin/P095_WriteCommandTriggerOptions.png differ diff --git a/docs/source/Plugin/P095_commands.repl b/docs/source/Plugin/P095_commands.repl index 0333daf36..e88f9821e 100644 --- a/docs/source/Plugin/P095_commands.repl +++ b/docs/source/Plugin/P095_commands.repl @@ -1,69 +1,40 @@ .. csv-table:: - :header: "Command", "Extra information" - :widths: 20, 30 + :header: "Command Syntax", "Extra information" + :widths: 20, 30 - " - ``TFTCMD,`` - "," - Control the screen (on, off, clear,..) + " + This plugin allows dynamic configuration of the command-trigger. Available triggers are: - Examples: + * ``tft`` + * ``ili9341`` + * ``ili9342`` + * ``ili9481`` + * ``ili9486`` + * ``ili9488`` - - ``tftcmd,on`` Switch display on. - - ``tftcmd,off`` Switch display off. - - ``tftcmd,clear`` Clear whole display. - - ``tftcmd,clear,green`` Clear whole display with green color. - - ``tftcmd,inv,1`` Invert the dispaly (value:0 normal display, 1 inverted display) - - ``tftcmd,rot,2`` Rotate display (value from 0 to 3 inclusive) - " - " - ``TFT,,....`` - "," - Draw line, rect, circle, triangle and text - - Subcommands: - - - ``tft,txt,`` Write simple text (use last position, color and size) - - ``tft,txp,,`` Set text position (move the cursor) - - ``tft,txc,,`` Set text color (background is transparent if not provided - - ``tft,txs,`` Set text size - - ``tft,txtfull,,,,,,`` Write text with all options - - ``tft,l,,,<2>,,`` Draw a simple line - - ``tft,lh,,,`` Draw an horizontal line (width = Line width in pixels (positive = right of first point, negative = point of first corner). - - ``tft,lv,,,`` Draw a vertical line (height= Line height in pixels (positive = below first point, negative = above first point). - - ``tft,r,,,,,`` Draw a rectangle - - ``tft,rf,,,,,,`` Draw a filled rectangle - - ``tft,c,,,,`` Draw a circle - - ``tft,cf,,,,,`` Draw a filled circle - - ``tft,t,,,,,,,`` Draw a triangle - - ``tft,tf,,,,,,,,`` Draw a filled triangle - - ``tft,rr,,,,,,`` Draw a round rectangle - - ``tft,rrf,,,,,,,`` Draw a filled round rectangle - - ``tft,px,,,`` Print a single pixel - - ``tft,font,`` Switch to font - SEVENSEG24, SEVENSEG18, FREESANS, DEFAULT - " - -Examples: + The default trigger is ``tft`` + "," + Some commands are postfixed with ``cmd``, indicating device specific commands. Not device-specific commands are genericly applicable to all *Adafruit Graphics* enabled devices, using the ESPEasy ``AdafruitGFX_helper`` module. -Write Text : - ``tft,txtfull,0,0,HelloWorld`` - -Write Text another place: - ``tft,txtfull,100,40,HelloWorld`` - -Write bigger Text : - ``tft,txtfull,0,0,3,HelloWorld`` - -Write RED Text : - ``tft,txtfull,0,0,3,HelloWorld`` - -Write RED Text (size is 1): - ``tft,txtfull,0,0,1,RED,HelloWorld`` - -Write RED Text on YELLOW background (size is 1): - ``tft,txtfull,0,0,1,RED,YELLOW,HelloWorld`` - -Select font : - -- ``tft,font,SEVENSEG24`` -- ``tft,txtfull,0,0,1,RED,17.0`` + If one of the alternative **Write Command trigger** options is selected, then also the plugin specific commands change from ``tftcmd`` to f.e. ``ili9481cmd``. + " + " + | ``tftcmd,on`` + "," + | Switch the display on. + " + " + | ``tftcmd,off`` + "," + | Switch the display off. If the Backlight is connected and configured, that will also be turned off. + " + " + | ``tftcmd,clear`` + "," + | Clear the display, using the default background color. + " + " + | ``tftcmd,backlight,`` + "," + | Set the level of backlight brightness, from 1..100%. Only applicable if a backlight pin is configured. + " diff --git a/docs/source/Plugin/P128.rst b/docs/source/Plugin/P128.rst index 322fa4fa0..3c564603f 100644 --- a/docs/source/Plugin/P128.rst +++ b/docs/source/Plugin/P128.rst @@ -33,6 +33,11 @@ LED strips (sometimes called stripes) using the WS281x type (and clones) of mult |P128_usedby| +Power consumption +~~~~~~~~~~~~~~~~~ + +.. include:: NeoPixel_Power.rst + Configuration ------------- diff --git a/docs/source/Plugin/P129.rst b/docs/source/Plugin/P129.rst new file mode 100644 index 000000000..8cd6aca94 --- /dev/null +++ b/docs/source/Plugin/P129.rst @@ -0,0 +1,138 @@ +.. include:: ../Plugin/_plugin_substitutions_p12x.repl +.. _P129_page: + +|P129_typename| +================================================== + +|P129_shortinfo| + +Plugin details +-------------- + +Type: |P129_type| + +Name: |P129_name| + +Status: |P129_status| + +GitHub: |P129_github|_ + +Maintainer: |P129_maintainer| + +Used libraries: |P129_usedlibraries| + +Description +----------- + +The 74HC165 Shift registers can be used to connect many input pins to an ESP using only 3 or 4 GPIO pins, as these shift registers can easily be daisy chained. + +When connecting many shift registers daisy-chained, it is important to ensure that not *too* many are connected to a single GPIO output pin, as that could overload that pin. It is advised to use a buffer chip for all used GPIOs for every 4 to 5 shift registers. When using a Schmitt-trigger buffer, it will also clean up the signal further down the line. Any buffer will introduce a small delay in the signal, possibly reducing the number of shift registers that can be controlled. A few useful answers are in this `Stack Exchange question`_ (issue is similar for input and output shift registers) + +The input pins can be used to read f.e. switches, like contacts along a model-train track, or reed contacts. Any debouncing should be done in hardware by adding a capacitor at the switch. + +Configuration +------------- + +.. image:: P129_DeviceConfiguration.png + :alt: Device configuration + +* **Name**: A unique name should be entered here. + +* **Enabled**: The device can be disabled or enabled. When not enabled the device should not use any resources. + +Actuator +^^^^^^^^ + +* **GPIO -> Data (Q7)**: Select the GPIO pin that is connected to the Data out pin of the (first) shift register. Any daisy chained register has its ``DS`` pin connected to the ``Q7`` pin of the previous shift register. + +* **GPIO -> Clock (CP)**: Select the Clock pin of all shift registers. + +* **GPIO -> Enable (EN) (optional)**: Select the Enable pin of all shift registers, or set to None if not used or not available. + +* **GPIO -> Load (PL)**: Select the Load pin of all shift registers. + +NB: At least **Data**, **Clock** and **Load** GPIO pins need to be configured for the plugin to be able to function as intended. + +Device configuration +^^^^^^^^^^^^^^^^^^^^ + +* **Number of chips (Q7 -> DS)**: The number of chips daisy-chained to each other. Default: 1, max. 16 (compile-time defined, technically limited to 16), allowing for max. 128 input pins. When changing the number of chips, the page will be submitted and reloaded to update the Event configuration. + +* **Sample frequency**: The interval of reading the inputs from the shift registers, 10 times per second or 50 times per second. + +.. image:: P129_SampleFrequencyOptions.png + :alt: Sample frequency options + +Display and output +^^^^^^^^^^^^^^^^^^ + +* **Values display (Off=Hex/On=Bin)**: The display on the Devices page is custom formatted to show the state of the inputs of the shift registers in either Hex (default) or Bin format, having a separator per chip (8 bits) for readability. + +All zeroes, Hex display (default) + +.. image:: P129_ValuesHex.png + :alt: Devices page, hexadecimal Values example + +All zeroes, Bin display + +.. image:: P129_ValuesBin.png + :alt: Devices page, binary Values example + +NB: The State values are grouped per 4 chips. To be read from right to left in chip-order, see below. If less chips are configured, only available states are shown. + +.. image:: P129_ValuesBinChip.png + :alt: Devices page, binary Values pointing out chip numbers + +* **Output selection**: Choose the values that are sent out to any controllers configured, or when getting the value from processing ``[#]``. + +These are the available options: + +.. image:: P129_OutputOptions.png + :alt: Output selection options + +If ``Decimal & hex/bin`` is selected, the value is output twice, formatted as decimal and as hexadecimal (``0x`` prefix) or binary (``0b`` prefix), separated by a comma. + +If either of the Hex/bin options is selected, but no ``Values display (Off=Hex/On=Bin)`` option is available, then the output will be formatted as hexadecimal. + +* **Separate events per pin**: When enabled, sends separate events per pin, including the pin number, to be processed via rules. F.e. ``#=,,,``. When disabled, all pins can be handled by a single rule, as it's generated like: ``=,,,``. + +Event configuration +^^^^^^^^^^^^^^^^^^^ + +To select for which input an event should be generated, a matrix is shown, listing the pins per chip that can be separately enabled. + +When all 16 chips are configured, the configuration looks like this: + +.. image:: P129_EventConfiguration.png + :alt: Event configuration + +The Data Acquisition, Send to Controller and Interval settings are standard available configuration items. Send to Controller only when one or more Controllers are configured. + +* **Interval** By default, Interval will be set to 0 sec. as this is optional. Every Interval setting, the current output states will be read from the runtime object (*not* from the shift registers!) and stored in the Values fields, so they can be restored on a warm boot of the ESP. + +Values +^^^^^^ + +The Values ``State_A`` to ``State_D`` are used to show the state of up to 16 shift registers. (Custom formatted like set in **Output selection**.) Values that are not going to be filled are not shown here, and no events will be generated either. + +Commands +~~~~~~~~ + +.. include:: P129_commands.repl + + +Events +~~~~~~ + +.. include:: P129_events.repl + + + +Change log +---------- + +.. versionchanged:: 2.0 + + |added| 2022-02-23 Added to ESPEasy as a new plugin. + +.. _Stack Exchange question: https://electronics.stackexchange.com/questions/40242/what-problems-could-occur-when-chaining-40-shift-registers \ No newline at end of file diff --git a/docs/source/Plugin/P129_DeviceConfiguration.png b/docs/source/Plugin/P129_DeviceConfiguration.png new file mode 100644 index 000000000..8cb649202 Binary files /dev/null and b/docs/source/Plugin/P129_DeviceConfiguration.png differ diff --git a/docs/source/Plugin/P129_EventConfiguration.png b/docs/source/Plugin/P129_EventConfiguration.png new file mode 100644 index 000000000..32d3de0d1 Binary files /dev/null and b/docs/source/Plugin/P129_EventConfiguration.png differ diff --git a/docs/source/Plugin/P129_OutputOptions.png b/docs/source/Plugin/P129_OutputOptions.png new file mode 100644 index 000000000..19e200f03 Binary files /dev/null and b/docs/source/Plugin/P129_OutputOptions.png differ diff --git a/docs/source/Plugin/P129_SampleFrequencyOptions.png b/docs/source/Plugin/P129_SampleFrequencyOptions.png new file mode 100644 index 000000000..5b94ce7fa Binary files /dev/null and b/docs/source/Plugin/P129_SampleFrequencyOptions.png differ diff --git a/docs/source/Plugin/P129_ValuesBin.png b/docs/source/Plugin/P129_ValuesBin.png new file mode 100644 index 000000000..e022307d6 Binary files /dev/null and b/docs/source/Plugin/P129_ValuesBin.png differ diff --git a/docs/source/Plugin/P129_ValuesBinChip.png b/docs/source/Plugin/P129_ValuesBinChip.png new file mode 100644 index 000000000..b4750d851 Binary files /dev/null and b/docs/source/Plugin/P129_ValuesBinChip.png differ diff --git a/docs/source/Plugin/P129_ValuesHex.png b/docs/source/Plugin/P129_ValuesHex.png new file mode 100644 index 000000000..95d7e1ea7 Binary files /dev/null and b/docs/source/Plugin/P129_ValuesHex.png differ diff --git a/docs/source/Plugin/P129_commands.repl b/docs/source/Plugin/P129_commands.repl new file mode 100644 index 000000000..c5990ca50 --- /dev/null +++ b/docs/source/Plugin/P129_commands.repl @@ -0,0 +1,31 @@ +The commands are all used to update configuration, not directly control the state of the shift registers. + +.. csv-table:: + :header: "Command Syntax", "Extra information" + :widths: 20, 30 + + " + | ``ShiftIn,PinEvent,,<0|1>`` + "," + | Enables or disables an event to be generated for the pin. Is updated in the configuration, but not saved. + " + " + | ``ShiftIn,ChipEvent,,<0|1>`` + "," + | Enables or disables an event to be generated for all pins of the selected chip. Is updated in the configuration, but not saved. + " + " + | ``ShiftIn,SetChipCount,`` + "," + | Set the number of chips connected together. Is updated in the configuration, but not saved. + " + " + | ``ShiftIn,SampleFrequency,<0|1>`` + "," + | Select the sample frequency, 0 = 10/sec, 1 = 50/sec. + " + " + | ``ShiftIn,EventPerPin,<0|1>`` + "," + | Set or reset the ``Separate event per pin`` option. Is updated in the configuration, but not saved. + " diff --git a/docs/source/Plugin/P129_events.repl b/docs/source/Plugin/P129_events.repl new file mode 100644 index 000000000..ebbc0b6ea --- /dev/null +++ b/docs/source/Plugin/P129_events.repl @@ -0,0 +1,43 @@ +.. csv-table:: + :header: "Event", "Extra information" + :widths: 20, 30 + + " + | ``=,,,`` + + | ``state``: ``0`` or ``1``. + + | ``chip``: range 1..16, depending on the number of chips configured. + + | ``port``: range 1..8. + + | ``pin``: range 1..128, depending on the number of chips configured. + "," + | Generic event for each activated input pin, that is enabled in the **Event configuration** matrix. + + | Generated if **Separate events per pin** is *unchecked*/*disabled*. + " + " + | ``#=,,,`` + + | ``state``: ``0`` or ``1``. + + | ``chip``: range 1..16, depending on the number of chips configured. + + | ``port``: range 1..8. + + | ``pin``: range 1..128, depending on the number of chips configured. + "," + | Generic event for each activated input pin, that is enabled in the **Event configuration** matrix. + + | Generated if **Separate events per pin** is **enabled**. + " + " + | ``#=[][,][]`` + + | Values provided depend on the setting **Output selection**. + "," + | The generic event per Value ``State_A`` .. ``State_D``, that are available, depending on the number of chips configured. + + | Only sent if **Interval** is greater than 0, or an explicit ``TaskRun`` command for this task is executed. + " \ No newline at end of file diff --git a/docs/source/Plugin/P131.rst b/docs/source/Plugin/P131.rst new file mode 100644 index 000000000..f94984e54 --- /dev/null +++ b/docs/source/Plugin/P131.rst @@ -0,0 +1,273 @@ +.. include:: ../Plugin/_plugin_substitutions_p13x.repl +.. _P131_page: + +|P131_typename| +================================================== + +|P131_shortinfo| + +Plugin details +-------------- + +Type: |P131_type| + +Name: |P131_name| + +Status: |P131_status| + +GitHub: |P131_github|_ + +Maintainer: |P131_maintainer| + +Used libraries: |P131_usedlibraries| + +Description +----------- + +The popular NeoPixel multi-color leds are available in many configurations, either as separate leds or led-stripes and also in a pre-mounted matrix configuration. They are easy to implement, as they only require a single GPIO pin to control up to several hundreds of these pixels. + +This plugin is created to use 1 or more matrices of NeoPixels, and allows to draw graphic elements, write text or display bmp files, with a configurable brightness. + +When using multiple matrices, we use the term 'Tile' in this context, they need to use the same layout and rectangular size per matrix. The Tiles are also configurable in layout and rectangular size. + +This plugin uses the same AdafruitGFX_Helper module, as used in some other Display plugins, like :ref:`P095_page` and :ref:`P116_page`, and implements the same graphic commands. The AdafruitGFX_Helper module is 'limited' to 65535 (RGB565 coded) colors, but the used library uses a smart color mapping scheme to display colors as accurate as possible. + +Matrix and Tile layouts +~~~~~~~~~~~~~~~~~~~~~~~ + +Several Matrix and Tile layout configurations are possible, and supported. Reasons for having a specific layout are usually driven by the challenges of mechanical construction for these types of display. Common requirement is that all matrices and tiles are electrically connected as a single, uninterrupted, serial stream of NeoPixel leds. + +.. note:: **Credits**: Most of this layout-description and the images are based on and copied from the documentation available at the `Adafruit NeoPixel Uber-guide `_ + +**Matrix**: A Matrix is a rectangular set of NeoPixel leds, with configurable width, height, starting led, orientation and direction. + +**Tile**: A Tile is a rectangular set of same size/layout Matrices, with configurable width, height, starting matrix, orientation and direction. + +The plugin handles both single matrices - all NeoPixels in a single uniform grid - and tiled matrices - multiple matrices combined into a larger display: + +.. image:: P131_leds_single-vs-tiled.png + :alt: Leds single versus tiled + +Let's begin with the declaration for a single matrix, because it's simpler to explain. We'll be using an 8*5 matrix of NeoPixels. When looking at this matrix with the text in a readable orientation, the first pixel, #0, is at the top left. Each successive pixel is right one position - pixel 1 is directly to the right of pixel 0, and so forth. At the end of each row, the next pixel is at the left side of the next row. This isn't something we decide in code... it's how the NeoPixels are hard-wired in this circuit board comprising the matrix. + +.. image:: P131_leds_shield-horiz.jpg + :alt: Matrix with horizontal layout + +The first pixel is in Top/Left postition. We refer to this layout as Row mode and Progressive. Row mode means the pixels are arranged in horizontal lines (the opposite, in vertical lines, is column mode). Progressive means each row proceeds in the same direction. Some matrices will reverse direction on each row, as it can be easier to wire that way, especially when using a very wide matrix. We call that a zigzag layout. + +However, if we want to use this matrix in the "tall" direction, so the display is standing up on the desk with the ``Din`` connection at the right-top. When we turn the board this way, the matrix layout changes: + +.. image:: P131_leds_shield-vert.jpg + :alt: Matrix with vertical layout + +Now the first pixel is at Top/Right. Pixels increment top-to-bottom - it's now in Column mode. The flow-direction of the columns is still Progressive though. + +Similar configuration options are available for tiles. Making more specific mounting configurations possible, like this: + +.. image:: P131_leds_hairy.png + :alt: Specific Matrix and Tile configuration + +As shown in these examples, neither a Matrix nor a Tile has to be square, though both have to be rectangular to result in a correct display setup. + +NB: The maximum number of matrices/NeoPixels that can be connected is not determined, though when having > 1500 NeoPixels on a single GPIO pin, this can cause timing issues when the ESP is not fast enough to update all leds within the available time-window, resulting in the content being distorted. + +Signal wiring +~~~~~~~~~~~~~ + +.. note:: If the matrices use a VCC higher than 3.3V, f.e. 5V (and they usually do), a level converter for 3V3 to 5V should be used when connecting the display to the ESP, as the ESP might be damaged, or the logic levels don't match the requirements of the display (high level ~70% of VCC = 3.5V @ VCC 5V), and may not work properly. + +.. code-block:: text + + ESP Level converter 5V NeoMatrix 1 5V NeoMatrix 2 5V NeoMatrix n + ---- --------------- | ----------- | ----------- | ----------- + 3V3 --> LV HV <-+-> VCC +-> VCC +-> VCC ... + GND --- GND ------- GND --- GND - GND --- GND - GND --- GND ... + GPIO --> LV1 ------> HV1 -[470]- DIN - DOUT --> DIN - DOUT --> DIN ... + +NB: It is advised to use a single 220 to 470 ohm resistor in the wiring from ESP (after the level converter) to the first NeoPixel ``Din`` for better signal stability. + +Power consumption +~~~~~~~~~~~~~~~~~ + +.. include:: NeoPixel_Power.rst + +Also, when using several matrices sequentially, separate wiring should be added for VCC and GND to each group of matrices, to avoid burning out the pcb traces of the matrix where the power is connected. For groups of 4 to 8 matrices, it is advised to use separate wiring for power to each group, and only connect the GND and signal connections Dout/GND to Din/GND of the next group, but **never** the VCC pins. + + +Configuration +------------- + +.. image:: P131_DeviceConfiguration.png + :alt: Device configuration + +Task settings +~~~~~~~~~~~~~ + +* **Name** The name of the task. This should be unique for all devices that are configured. (Initially empty) + +* **Enabled** For the device to work it has to be enabled. When checked, the device will be started as soon as the ESP starts. If desired, the device can also be enabled from f.e. a rule by using the ``TaskEnable,`` or ``TaskEnable,`` command, or disabled using the corresponding ``TaskDisable,|`` commands. + +Actuator +~~~~~~~~ + +* **GPIO -> DIN** Select the GPIO pin that's wired to the ``DIN`` input on the first matrix. + +* **Strip Type**: Select the type of the LEDs mounted on the matrices: + +.. image:: P131_StripTypeOptions.png + :alt: The Strip type options + +*GRB* : Most commonly, the LEDs are of GRB type. + +*GRBW* : If the LEDs also support Brightness control, then the GRBW type should be selected. + + +Matrix +~~~~~~ + +* **Matrix width**: The number of NeoPixels that form the width of 1 Matrix. Range: 1..100. +* **Matrix height**: The number of NeoPixels that form the height of 1 Matrix. Range: 1..100. +* **Matrix start-pixel**: The location of the first NeoPixel of the Matrix. Available options: + +.. image:: P131_StartOptions.png + :alt: Start options + +* **Matrix Rows/Columns mode**: The direction of how the pixels are installed in the Matrix. Available options: + +.. image:: P131_RowsColumnsOptions.png + :alt: Rows/Columns mode options + +* **Matrix flow-direction**: The flow of the pixels on the Matrix. Available options: + +.. image:: P131_FlowDirectionOptions.png + :alt: Flow-direction options + +Multiple matrices: Tiles +~~~~~~~~~~~~~~~~~~~~~~~~ + +* **Tile width**: The number of Matrices that form the width of the Tile. Range: 1..32. +* **Tile height**: The number of Matrices that form the height of the Tile. Range: 1..32. +* **Tile start-matrix**: The location of the first Matrix in the Tile. Available options: + +.. image:: P131_StartOptions.png + :alt: Start options + +* **Matrix Rows/Columns mode**: The direction of how the Matrices are connected in the Tile. Available options: + +.. image:: P131_RowsColumnsOptions.png + :alt: Rows/Columns mode options + +* **Matrix flow-direction**: The flow of the Matrices in the Tile. Available options: + +.. image:: P131_FlowDirectionOptions.png + :alt: Flow-direction options + +Display +~~~~~~~ + +* **Rotation** Depending on how the display is to be mounted/installed, it may be needed to rotate the content, or with a non-square resolution, to use the display in *Landscape* layout instead of the default *Portrait*. + +Available options: + +.. image:: P131_RotationOptions.png + :alt: Rotation + +The available options rotate the display in a clock-wise direction. + +NB: Because of the above Matrix & Tile configuration, this option is less relevant for this display, but available in the AdafruitGFX_Helper module, so still included here as the accompanying ``rot`` subcommand is also supported. + +* **Text print Mode** Defines the handling of text when predefined text or text via the ``txt`` or ``txtfull`` subcommands (see below) is displayed. + +Available options: + +.. image:: P131_TextPrintModeOptions.png + :alt: Text print mode + +* *Continue to next line* Simply 'prints' all text to the display, with automatic overflow to the next line(s) if the text doesn't fit on the current line. +* *Truncate exceeding message* (Default) Display the provided message, but any excess at the right border of the display is discarded. +* *Clear then truncate exceeding message* Clear from the current cursor position to the width of the display, then display the provided message, but any excess at the right border of the display is discarded. + +Default setting is *Continue to next line*. + +* **Initial brightness**: The brightness that the display is initially set to. Default is 40, to protect both the eyes and the power-supply powering the display. Range: 0..255. + +* **Maximum allowed brightness**: The brightness that is the maximum it can be set to, either from UI or via the ``brightness`` command, to optionally help protect both the eyes and the power-supply powering the display. Default is 255. Range: 1..255. + +* **Font scaling** The scaling factor for the currently active font. Select a factor between 1 and 4. + +* **Clear display on exit** When checked, will clear the display when the task is disabled, either from settings or via the ``TaskDisable`` command. This will fill the display with black pixels, turning all NeoPixels off. + +* **Write Command trigger** The command to handle any commands for this device can be selected here. This way you can have different commands when multiple displays are configured. It is processing most commands via the ESPEasy Adafruit Graphics helper class. + +Available options: + +.. image:: P131_WriteCommandTriggerOptions.png + :alt: Write-command trigger + +* *neomatrix* +* *neo* + +The command is handled non-case sensitive. See below for available commands and subcommands. + +Content +^^^^^^^^ + +* **Foreground color** +* **Background color** + +These are the default colors, used to display the content as configured below (if any). + +Colors can be specified in 3 ways: + +* *Color name* Some named colors are recognized: White, Black, Red, Green, Blue, Yellow, Orange, Navy, Darkgreen, Cyan, Darkcyan, Maroon, Purple, Olive, Magenta, Lightgrey, Darkgrey, Pink and Greenyellow. If any of these colors is recognized, the name will be shown. +* *#RGB565* A ``#`` prefixed, 1 to 4 digit, hex-coded color value in RGB565 format (5 bits red, 6 bits green, 5 bits blue) giving the max. number of 65535 colors. This is the value that is shown after the settings are saved, and it is not one of the known color names. +* *#RRGGBB* A ``#`` prefixed, 6 digit, hex coded RGB color value. Will be transformed to an RGB565 value on save! + +If the Foreground and Background colors are the same, the background color will become ``transparent``. If the Foreground color is empty, as a default ``white`` will be set. + +Lines +^^^^^ + +* **Line 1..n** Predefined content can be specified. The number of lines shown depends on the Tile height of the display. + +The usual variables, like ``[Taskname#Valuename]``, or ``%v1%``, system variables, formulas and functions can be used. + +Input length is limited to 50 characters per line. If a longer calculated text is needed for a line, then it should be set from the rules or from an external source via http commands. + +Per **Line** you can define if Scrolling should be used for displaying the content. When enabled, the other available options will be used to scroll the text. + +* **Scroll**: Enables (horizontal) scrolling of the content. NB: Scrolling will *only* be enabled if the content length won't fit on the display! +* **Empty start**: When checked, the display will start empty, and the text will be scrolled in. It will also scroll out until the display is empty again. (Default enabled). When unchecked, the display will start with the content that fits on the display, and scroll until the 'other' end of the text is visible. The display won't be empty when having this setting off. +* **Scroll right**: By default, scrolling will be from right to left, when this option is checked, scrolling will be from left to right direction. +* **Stepsize**: The number of pixels that each step will scroll. Range 1..16, default 1. +* **Speed**: The number of 0.1 seconds before the next scroll step will be applied. Range 1..600 (0.1 second to 60 seconds), default 10 (1 step per second). + +If more then 1 line is shown, next to the last Line the remaining capacity in characters is displayed. + +The total combination of lines * input length can not exceed 1000 characters (sized dynamically), as there is limited storage per task available for these settings. An error message will be shown after (trying to) save the settings, **any excess content will be discarded!** + +Example: + +.. image:: P131_SaveError.png + :alt: Save error + +* **Interval** By default, Interval will be set to 0. If set to a non-zero value, the pre-configured content will be updated automatically using that interval (seconds). Depending on the **Text print Mode** setting, content that may have been draw from rules or external commands, may be erased. + + + +Commands +~~~~~~~~ + +.. include:: P131_commands.repl + +.. include:: AdaGFX_commands.repl + + + +Change log +---------- + +.. versionchanged:: 2.0 + ... + + |added| 2022-04-16 New plugin. diff --git a/docs/source/Plugin/P131_DeviceConfiguration.png b/docs/source/Plugin/P131_DeviceConfiguration.png new file mode 100644 index 000000000..49a528a6a Binary files /dev/null and b/docs/source/Plugin/P131_DeviceConfiguration.png differ diff --git a/docs/source/Plugin/P131_FlowDirectionOptions.png b/docs/source/Plugin/P131_FlowDirectionOptions.png new file mode 100644 index 000000000..4e7605df7 Binary files /dev/null and b/docs/source/Plugin/P131_FlowDirectionOptions.png differ diff --git a/docs/source/Plugin/P131_RotationOptions.png b/docs/source/Plugin/P131_RotationOptions.png new file mode 100644 index 000000000..3111d00db Binary files /dev/null and b/docs/source/Plugin/P131_RotationOptions.png differ diff --git a/docs/source/Plugin/P131_RowsColumnsOptions.png b/docs/source/Plugin/P131_RowsColumnsOptions.png new file mode 100644 index 000000000..574895252 Binary files /dev/null and b/docs/source/Plugin/P131_RowsColumnsOptions.png differ diff --git a/docs/source/Plugin/P131_SaveError.png b/docs/source/Plugin/P131_SaveError.png new file mode 100644 index 000000000..f17023400 Binary files /dev/null and b/docs/source/Plugin/P131_SaveError.png differ diff --git a/docs/source/Plugin/P131_StartOptions.png b/docs/source/Plugin/P131_StartOptions.png new file mode 100644 index 000000000..d15171996 Binary files /dev/null and b/docs/source/Plugin/P131_StartOptions.png differ diff --git a/docs/source/Plugin/P131_StripTypeOptions.png b/docs/source/Plugin/P131_StripTypeOptions.png new file mode 100644 index 000000000..1a7656045 Binary files /dev/null and b/docs/source/Plugin/P131_StripTypeOptions.png differ diff --git a/docs/source/Plugin/P131_TextPrintModeOptions.png b/docs/source/Plugin/P131_TextPrintModeOptions.png new file mode 100644 index 000000000..95599714a Binary files /dev/null and b/docs/source/Plugin/P131_TextPrintModeOptions.png differ diff --git a/docs/source/Plugin/P131_WriteCommandTriggerOptions.png b/docs/source/Plugin/P131_WriteCommandTriggerOptions.png new file mode 100644 index 000000000..019bcab9b Binary files /dev/null and b/docs/source/Plugin/P131_WriteCommandTriggerOptions.png differ diff --git a/docs/source/Plugin/P131_commands.repl b/docs/source/Plugin/P131_commands.repl new file mode 100644 index 000000000..841236ba4 --- /dev/null +++ b/docs/source/Plugin/P131_commands.repl @@ -0,0 +1,60 @@ +.. csv-table:: + :header: "Command Syntax", "Extra information" + :widths: 20, 30 + + " + This plugin allows dynamic configuration of the command-trigger. Available triggers are: + + * ``neomatrix`` + * ``neo`` + + The default trigger is ``neomatrix`` + "," + Some commands are postfixed with ``cmd``, indicating device specific commands. Not device-specific commands are genericly applicable to all *Adafruit Graphics Helper* enabled devices, using the ESPEasy ``AdafruitGFX_helper`` module. + " + " + | ``neomatrixcmd,clear`` + "," + | Clear the display, using the default background color. + " + " + | ``neomatrixcmd,bright[,]`` + "," + | Set the level of display brightness, range: 1..255. If no brightness or 0 is provided, the Initial brightness setting is used. + + | The maximum brightness allowed is determined by the Maximum allowed brightness setting. + " + " + | ``neomatrixcmd,settext,,`` + "," + | Set the text for a specific line of the matrix. When Scroll is enabled for that line, the text will be scrolled as configured. + " + " + | ``neomatrixcmd,setscroll,,<0|1>`` + "," + | Set scrolling off or on for the text of a specific line of the matrix. When Scroll is enabled the text will be scrolled if it doesn't fit on the display. + " + " + | ``neomatrixcmd,setempty,,<0|1>`` + "," + | Set scrolling with empty start off or on for the text of a specific line of the matrix. + " + " + | ``neomatrixcmd,setright,,<0|1>`` + "," + | Set scrolling to right off or on for the text of a specific line of the matrix. + " + " + | ``neomatrixcmd,setstep,,<1..16>`` + "," + | Set the scroll step size for a line. Range is from 1 .. 16 pixels. + " + " + | ``neomatrixcmd,setspeed,,<1..600>`` + "," + | Set the scroll speed for a line. Range is from 1 .. 600 x 0.1 second (0.1 .. 1 minute/step). + " + " + | NB: The settings changed via ``neomatrixcmd,set*...`` commands will **not** be reflected in the device configuration! + "," + " diff --git a/docs/source/Plugin/P131_leds_hairy.png b/docs/source/Plugin/P131_leds_hairy.png new file mode 100644 index 000000000..9ea944a08 Binary files /dev/null and b/docs/source/Plugin/P131_leds_hairy.png differ diff --git a/docs/source/Plugin/P131_leds_shield-horiz.jpg b/docs/source/Plugin/P131_leds_shield-horiz.jpg new file mode 100644 index 000000000..78a998ae2 Binary files /dev/null and b/docs/source/Plugin/P131_leds_shield-horiz.jpg differ diff --git a/docs/source/Plugin/P131_leds_shield-vert.jpg b/docs/source/Plugin/P131_leds_shield-vert.jpg new file mode 100644 index 000000000..14c744c41 Binary files /dev/null and b/docs/source/Plugin/P131_leds_shield-vert.jpg differ diff --git a/docs/source/Plugin/P131_leds_single-vs-tiled.png b/docs/source/Plugin/P131_leds_single-vs-tiled.png new file mode 100644 index 000000000..d94b32df7 Binary files /dev/null and b/docs/source/Plugin/P131_leds_single-vs-tiled.png differ diff --git a/docs/source/Plugin/_Plugin.rst b/docs/source/Plugin/_Plugin.rst index 6ea922412..b4aaf4ab5 100644 --- a/docs/source/Plugin/_Plugin.rst +++ b/docs/source/Plugin/_Plugin.rst @@ -354,6 +354,8 @@ There are different released versions of ESP Easy: ":ref:`P126_page`","|P126_status|","P126" ":ref:`P127_page`","|P127_status|","P127" ":ref:`P128_page`","|P128_status|","P128" + ":ref:`P129_page`","|P129_status|","P129" + ":ref:`P131_page`","|P131_status|","P131" ":ref:`P132_page`","|P132_status|","P132" ":ref:`P133_page`","|P133_status|","P133" @@ -453,6 +455,11 @@ Plugins: |Plugin_Hardware| Hardware: |P046_usedby| +Input +----- + +Plugins: |Plugin_Input| + Keypad ------ diff --git a/docs/source/Plugin/_plugin_categories.repl b/docs/source/Plugin/_plugin_categories.repl index a4cffab91..43fb36ff6 100644 --- a/docs/source/Plugin/_plugin_categories.repl +++ b/docs/source/Plugin/_plugin_categories.repl @@ -1,7 +1,7 @@ .. |Plugin_Analog_input| replace:: :ref:`P002_page`, :ref:`P007_page`, :ref:`P025_page`, :ref:`P060_page`, :ref:`P097_page` .. |Plugin_Acceleration| replace:: :ref:`P120_page`, :ref:`P125_page` .. |Plugin_Communication| replace:: :ref:`P016_page`, :ref:`P020_page`, :ref:`P035_page`, :ref:`P044_page`, :ref:`P054_page`, :ref:`P071_page`, :ref:`P087_page`, :ref:`P089_page`, :ref:`P094_page`, :ref:`P101_page`, :ref:`P118_page` -.. |Plugin_Display| replace:: :ref:`P012_page`, :ref:`P023_page`, :ref:`P036_page`, :ref:`P057_page`, :ref:`P073_page`, :ref:`P075_page`, :ref:`P095_page`, :ref:`P104_page`, :ref:`P116_page` +.. |Plugin_Display| replace:: :ref:`P012_page`, :ref:`P023_page`, :ref:`P036_page`, :ref:`P057_page`, :ref:`P073_page`, :ref:`P075_page`, :ref:`P095_page`, :ref:`P104_page`, :ref:`P116_page`, :ref:`P131_page` .. |Plugin_Distance| replace:: :ref:`P110_page`, :ref:`P113_page` .. |Plugin_Dust| replace:: :ref:`P018_page`, :ref:`P053_page`, :ref:`P056_page` .. |Plugin_Energy_AC| replace:: :ref:`P076_page`, :ref:`P077_page`, :ref:`P078_page`, :ref:`P102_page`, :ref:`P108_page` @@ -14,6 +14,7 @@ .. |Plugin_Gesture| replace:: :ref:`P064_page` .. |Plugin_Gyro| replace:: :ref:`P045_page`, :ref:`P119_page` .. |Plugin_Hardware| replace:: :ref:`P046_page` +.. |Plugin_Input| replace:: :ref:`P129_page` .. |Plugin_Keypad| replace:: :ref:`P058_page`, :ref:`P061_page`, :ref:`P062_page`, :ref:`P063_page` .. |Plugin_Light_Color| replace:: :ref:`P050_page`, :ref:`P066_page` .. |Plugin_Light_UV| replace:: :ref:`P084_page`, :ref:`P107_page`, :ref:`P114_page`, :ref:`P133_page` diff --git a/docs/source/Plugin/_plugin_substitutions_p09x.repl b/docs/source/Plugin/_plugin_substitutions_p09x.repl index eafc54714..71708fe21 100644 --- a/docs/source/Plugin/_plugin_substitutions_p09x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p09x.repl @@ -59,9 +59,9 @@ .. |P094_compileinfo| replace:: `.` .. |P094_usedlibraries| replace:: `.` -.. |P095_name| replace:: :cyan:`TFT 2.4 inches ILI9341` +.. |P095_name| replace:: :cyan:`TFT ILI934x/ILI948x` .. |P095_type| replace:: :cyan:`Display` -.. |P095_typename| replace:: :cyan:`Display - TFT 2.4 inches ILI9341` +.. |P095_typename| replace:: :cyan:`Display - TFT ILI934x/ILI948x` .. |P095_porttype| replace:: `.` .. |P095_status| replace:: :yellow:`DISPLAY, COLLECTION` .. |P095_github| replace:: _P095_ILI9341.ino @@ -70,7 +70,7 @@ .. |P095_shortinfo| replace:: `.` .. |P095_maintainer| replace:: `TD-er` .. |P095_compileinfo| replace:: `.` -.. |P095_usedlibraries| replace:: `Adafruit GFX, Adafruit ILI9341` +.. |P095_usedlibraries| replace:: `Adafruit GFX, Adafruit ILI9341 (locally extended copy)` .. |P097_name| replace:: :cyan:`Touch ESP32` .. |P097_type| replace:: :cyan:`Internal` diff --git a/docs/source/Plugin/_plugin_substitutions_p12x.repl b/docs/source/Plugin/_plugin_substitutions_p12x.repl index 26293c4d0..069df90f5 100644 --- a/docs/source/Plugin/_plugin_substitutions_p12x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p12x.repl @@ -88,3 +88,16 @@ .. |P128_maintainer| replace:: `tonhuisman` .. |P128_compileinfo| replace:: `.` .. |P128_usedlibraries| replace:: `https://github.com/Makuna/NeoPixelBus and https://github.com/djcysmic/NeopixelBusFX (forked from ESPEasyPluginPlayground)` + +.. |P129_name| replace:: :cyan:`Shift registers (74HC165)` +.. |P129_type| replace:: :cyan:`Input` +.. |P129_typename| replace:: :cyan:`Input - Shift registers (74HC165)` +.. |P129_porttype| replace:: `.` +.. |P129_status| replace:: :yellow:`COLLECTION E` +.. |P129_github| replace:: P129_74HC165.ino +.. _P129_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P129_74HC165.ino +.. |P129_usedby| replace:: `.` +.. |P129_shortinfo| replace:: `Parallel - serial input shift registers` +.. |P129_maintainer| replace:: `tonhuisman` +.. |P129_compileinfo| replace:: `.` +.. |P129_usedlibraries| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p13x.repl b/docs/source/Plugin/_plugin_substitutions_p13x.repl index 61648f675..01441249f 100644 --- a/docs/source/Plugin/_plugin_substitutions_p13x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p13x.repl @@ -1,3 +1,16 @@ +.. |P131_name| replace:: :cyan:`NeoPixel Matrix` +.. |P131_type| replace:: :cyan:`Display` +.. |P131_typename| replace:: :cyan:`Display - NeoPixel Matrix` +.. |P131_porttype| replace:: `.` +.. |P131_status| replace:: :yellow:`NEOPIXEL` +.. |P131_github| replace:: P131_NeoPixelMatrix.ino +.. _P131_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P131_NeoPixelMatrix.ino +.. |P131_usedby| replace:: `.` +.. |P131_shortinfo| replace:: `NeoPixel Matrix display` +.. |P131_maintainer| replace:: `tonhuisman` +.. |P131_compileinfo| replace:: `.` +.. |P131_usedlibraries| replace:: `https://github.com/adafruit/Adafruit_NeoMatrix` + .. |P132_name| replace:: :cyan:`INA3221` .. |P132_type| replace:: :cyan:`Energy (DC)` .. |P132_typename| replace:: :cyan:`Energy (DC) - INA3221` diff --git a/docs/source/Reference/Command.rst b/docs/source/Reference/Command.rst index 660bc9ea8..2149ab9ad 100644 --- a/docs/source/Reference/Command.rst +++ b/docs/source/Reference/Command.rst @@ -701,3 +701,8 @@ P127 :ref:`P127_page` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. include:: ../Plugin/P127_commands.repl + +P129 :ref:`P129_page` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. include:: ../Plugin/P129_commands.repl diff --git a/docs/source/Reference/Events.rst b/docs/source/Reference/Events.rst index bb28ec1c7..8594b4c7d 100644 --- a/docs/source/Reference/Events.rst +++ b/docs/source/Reference/Events.rst @@ -522,3 +522,8 @@ P115 :ref:`P115_page` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. include:: ../Plugin/P115_events.repl + +P129 :ref:`P129_page` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. include:: ../Plugin/P129_events.repl diff --git a/esp32_partition_app1810k_spiffs2172k.csv b/esp32_partition_app1810k_spiffs2172k.csv new file mode 100644 index 000000000..7857763f1 --- /dev/null +++ b/esp32_partition_app1810k_spiffs2172k.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +# otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x1D0000, +# app1, app, ota_1, 0x1E0000,0x1000, +eeprom, data, 0x99, ,0x1000, +spiffs, data, spiffs, ,0x21F000, diff --git a/lib/Adafruit_ILI9341/.gitignore b/lib/Adafruit_ILI9341/.gitignore new file mode 100644 index 000000000..c2a26c038 --- /dev/null +++ b/lib/Adafruit_ILI9341/.gitignore @@ -0,0 +1,4 @@ +# Our handy .gitignore for automation ease +Doxyfile* +doxygen_sqlite3.db +html diff --git a/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp b/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp new file mode 100644 index 000000000..0a6d3a28a --- /dev/null +++ b/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp @@ -0,0 +1,857 @@ +/*! + * @file Adafruit_ILI9341.cpp + * + * @mainpage Adafruit ILI9341 TFT Displays + * + * @section intro_sec Introduction + * + * This is the documentation for Adafruit's ILI9341 driver for the + * Arduino platform. + * + * This library works with the Adafruit 2.8" Touch Shield V2 (SPI) + * http://www.adafruit.com/products/1651 + * + * Adafruit 2.4" TFT LCD with Touchscreen Breakout w/MicroSD Socket - ILI9341 + * https://www.adafruit.com/product/2478 + * + * 2.8" TFT LCD with Touchscreen Breakout Board w/MicroSD Socket - ILI9341 + * https://www.adafruit.com/product/1770 + * + * 2.2" 18-bit color TFT LCD display with microSD card breakout - ILI9340 + * https://www.adafruit.com/product/1770 + * + * TFT FeatherWing - 2.4" 320x240 Touchscreen For All Feathers + * https://www.adafruit.com/product/3315 + * + * 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! + * + * @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. + * + */ + +#include "Adafruit_ILI9341.h" +#ifndef ARDUINO_STM32_FEATHER +# include "pins_arduino.h" +# ifndef RASPI +# include "wiring_private.h" +# endif // ifndef RASPI +#endif // ifndef ARDUINO_STM32_FEATHER +#include + +#if defined(ARDUINO_ARCH_ARC32) || defined(ARDUINO_MAXIM) +# define SPI_DEFAULT_FREQ 16000000 + +// Teensy 3.0, 3.1/3.2, 3.5, 3.6 +#elif defined(__MK20DX128__) || defined(__MK20DX256__) || \ + defined(__MK64FX512__) || defined(__MK66FX1M0__) +# define SPI_DEFAULT_FREQ 40000000 +#elif defined(__AVR__) || defined(TEENSYDUINO) +# define SPI_DEFAULT_FREQ 8000000 +#elif defined(ESP8266) || defined(ESP32) +# define SPI_DEFAULT_FREQ 40000000 +#elif defined(RASPI) +# define SPI_DEFAULT_FREQ 80000000 +#elif defined(ARDUINO_ARCH_STM32F1) +# define SPI_DEFAULT_FREQ 36000000 +#else // if defined(ARDUINO_ARCH_ARC32) || defined(ARDUINO_MAXIM) +# define SPI_DEFAULT_FREQ 24000000 ///< Default SPI data clock frequency +#endif // if defined(ARDUINO_ARCH_ARC32) || defined(ARDUINO_MAXIM) + +#define MADCTL_MY 0x80 ///< Bottom to top +#define MADCTL_MX 0x40 ///< Right to left +#define MADCTL_MV 0x20 ///< Reverse Mode +#define MADCTL_ML 0x10 ///< LCD refresh Bottom to top +#define MADCTL_RGB 0x00 ///< Red-Green-Blue pixel order +#define MADCTL_BGR 0x08 ///< Blue-Green-Red pixel order +#define MADCTL_MH 0x04 ///< LCD refresh right to left +#define MADCTL_SS 0x02 ///< ?? ILI9481 mode +#define MADCTL_GS 0x01 ///< ?? ILI9481 mode + +/**************************************************************************/ + +/*! + @brief Instantiate Adafruit ILI9341 driver with software SPI + @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_ILI9341::Adafruit_ILI9341(int8_t cs, int8_t dc, int8_t mosi, +// int8_t sclk, int8_t rst, int8_t miso) +// : Adafruit_SPITFT(ILI9341_TFTWIDTH, ILI9341_TFTHEIGHT, cs, dc, mosi, sclk, +// rst, miso) {} + +/**************************************************************************/ + +/*! + @brief Instantiate Adafruit ILI9341 driver with hardware SPI using the + default SPI peripheral. + @param cs Chip select pin # (OK to pass -1 if CS tied to GND). + @param dc Data/Command pin # (required). + @param rst Reset pin # (optional, pass -1 if unused). + */ + +/**************************************************************************/ +Adafruit_ILI9341::Adafruit_ILI9341(int8_t cs, int8_t dc, int8_t rst, uint8_t model, uint16_t w, uint16_t h) + : Adafruit_SPITFT(w, h, cs, dc, rst) { + _model = model; + _w = w; + _h = h; +} + +#if !defined(ESP8266) + +/**************************************************************************/ + +/*! + @brief Instantiate Adafruit ILI9341 driver with hardware SPI using + a specific SPI peripheral (not necessarily default). + @param spiClass Pointer to SPI peripheral (e.g. &SPI or &SPI1). + @param dc Data/Command pin # (required). + @param cs Chip select pin # (optional, pass -1 if unused and + CS is tied to GND). + @param rst Reset pin # (optional, pass -1 if unused). + */ + +/**************************************************************************/ + +// Adafruit_ILI9341::Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs, +// int8_t rst) +// : Adafruit_SPITFT(ILI9341_TFTWIDTH, ILI9341_TFTHEIGHT, spiClass, cs, dc, +// rst) {} +#endif // end !ESP8266 + +/**************************************************************************/ + +/*! + @brief Instantiate Adafruit ILI9341 driver using parallel interface. + @param busWidth If tft16 (enumeration in Adafruit_SPITFT.h), is a + 16-bit interface, else 8-bit. + @param d0 Data pin 0 (MUST be a byte- or word-aligned LSB of a + PORT register -- pins 1-n are extrapolated from this). + @param wr Write strobe pin # (required). + @param dc Data/Command pin # (required). + @param cs Chip select pin # (optional, pass -1 if unused and CS + is tied to GND). + @param rst Reset pin # (optional, pass -1 if unused). + @param rd Read strobe pin # (optional, pass -1 if unused). + */ + +/**************************************************************************/ + +// Adafruit_ILI9341::Adafruit_ILI9341(tftBusWidth busWidth, int8_t d0, int8_t wr, +// int8_t dc, int8_t cs, int8_t rst, int8_t rd) +// : Adafruit_SPITFT(ILI9341_TFTWIDTH, ILI9341_TFTHEIGHT, busWidth, d0, wr, dc, +// cs, rst, rd) {} + +// clang-format off +static const uint8_t PROGMEM initcmd[] = { // ILI9341 & ILI9340 + 0xEF, 3, 0x03, 0x80, 0x02, + 0xCF, 3, 0x00, 0xC1, 0x30, + 0xED, 4, 0x64, 0x03, 0x12, 0x81, + 0xE8, 3, 0x85, 0x00, 0x78, + 0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, + 0xF7, 1, 0x20, + 0xEA, 2, 0x00, 0x00, + ILI9341_PWCTR1, 1, 0x23, // Power control VRH[5:0] + ILI9341_PWCTR2, 1, 0x10, // Power control SAP[2:0];BT[3:0] + ILI9341_VMCTR1, 2, 0x3e, 0x28, // VCM control + ILI9341_VMCTR2, 1, 0x86, // VCM control2 + ILI9341_MADCTL, 1, (MADCTL_MX | MADCTL_BGR), // Memory Access Control + ILI9341_VSCRSADD, 1, 0x00, // Vertical scroll zero + ILI9341_PIXFMT, 1, 0x55, + ILI9341_FRMCTR1, 2, 0x00, 0x18, + ILI9341_DFUNCTR, 3, 0x08, 0x82, 0x27, // Display Function Control + 0xF2, 1, 0x00, // 3Gamma Function Disable + ILI9341_GAMMASET, 1, 0x01, // Gamma curve selected + ILI9341_GMCTRP1, 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, + 0x08, + 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, + 0x09, 0x00, // Set Gamma + ILI9341_GMCTRN1, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, + 0x07, + 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, + 0x36, 0x0F, // Set Gamma + ILI9341_SLPOUT, 0x80, // Exit Sleep + ILI9341_DISPON, 0x80, // Display on + 0x00 // End of list +}; + +// clang-format on + +// clang-format off +static const uint8_t PROGMEM initcmd_9342[] = { // ILI9342 + 0xEF, 3, 0x03, 0x80, 0x02, + 0xCF, 3, 0x00, 0xC1, 0x30, + 0xED, 4, 0x64, 0x03, 0x12, 0x81, + 0xE8, 3, 0x85, 0x00, 0x78, + 0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, + 0xF7, 1, 0x20, + 0xEA, 2, 0x00, 0x00, + ILI9341_PWCTR1, 1, 0x23, // Power control VRH[5:0] + ILI9341_PWCTR2, 1, 0x10, // Power control SAP[2:0];BT[3:0] + ILI9341_VMCTR1, 2, 0x3e, 0x28, // VCM control + ILI9341_VMCTR2, 1, 0x86, // VCM control2 + ILI9341_MADCTL, 1, (MADCTL_MY | MADCTL_MV | MADCTL_BGR), // Memory Access Control + ILI9341_VSCRSADD, 1, 0x00, // Vertical scroll zero + ILI9341_PIXFMT, 1, 0x55, + ILI9341_FRMCTR1, 2, 0x00, 0x18, + ILI9341_DFUNCTR, 3, 0x08, 0x82, 0x27, // Display Function Control + 0xF2, 1, 0x00, // 3Gamma Function Disable + ILI9341_GAMMASET, 1, 0x01, // Gamma curve selected + ILI9341_GMCTRP1, 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, + 0x08, + 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, + 0x09, 0x00, // + // Set + // Gamma + ILI9341_GMCTRN1, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, + 0x07, + 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, + 0x36, 0x0F, // Set Gamma + ILI9341_SLPOUT, 0x80, // Exit Sleep + ILI9341_DISPON, 0x80, // Display on + 0x00 // End of list +}; + +// clang-format on + +// clang-format off +static const uint8_t PROGMEM initcmd_9481[] = { // ILI9481 + ILI9341_SLPOUT, 0x80, // Exit Sleep + 0xD0, 3, 0x07, 0x42, 0x18, + 0xD1, 3, 0x00, 0x07, 0x10, + 0xD2, 2, 0x01, 0x02, + ILI9341_PWCTR1, 5, 0x10, 0x3B, 0x00, 0x02, 0x11, // Power control VRH[5:0] + ILI9341_VMCTR1, 1, 0x03, // VCM control + 0xC8, 12, 0x00, 0x32, 0x36, 0x45, 0x06, 0x16, 0x37, + 0x75, 0x77, 0x54, 0x0C, 0x00, + ILI9341_MADCTL, 1, 0x0A, // Memory Access Control + ILI9341_PIXFMT, 1, 0x55, // Pixel format + // 0x55=16bit, 0x66=18bit + ILI9341_INVON, 0, + ILI9341_CASET, 4, 0x00, 0x00, 0x01, 0x3F, + ILI9341_PASET, 0x84, 0x00, 0x00, 0x01, 0xDF, + ILI9341_DISPON, 0x80, // Display on + 0x00 // End of list +}; + +// clang-format on + +// clang-format off +static const uint8_t PROGMEM initcmd_9481_CPT29[] = { // ILI9481 CPT29 variation + ILI9341_SLPOUT, 0x80, // Exit Sleep + 0xD0, 3, 0x07, 0x41, 0x1D, + 0xD1, 3, 0x00, 0x2b, 0x1f, + 0xD2, 2, 0x01, 0x11, + ILI9341_PWCTR1, 5, 0x10, 0x3B, 0x00, 0x02, 0x11, // Power control VRH[5:0] + ILI9341_VMCTR1, 1, 0x03, // VCM control + 0xC8, 12, 0x00, 0x14, 0x33, 0x10, 0x00, 0x16, 0x44, + 0x36, 0x77, 0x00, 0x0F, 0x00, + 0xB0, 1, 0x00, + 0xE4, 1, 0xA0, + 0xF0, 1, 0x01, + 0xF3, 2, 0x02, 0x1A, + ILI9341_MADCTL, 1, 0x0A, // Memory Access Control + ILI9341_PIXFMT, 1, 0x55, // Pixel format 0x55=16bit, + // 0x66=18bit + ILI9341_INVON, 0, + ILI9341_CASET, 4, 0x00, 0x00, 0x01, 0x3F, + ILI9341_PASET, 0x84, 0x00, 0x00, 0x01, 0xDF, + ILI9341_DISPON, 0x80, // Display on + 0x00 // End of list +}; + +// clang-format on + +// clang-format off +static const uint8_t PROGMEM initcmd_9481_PVI35[] = { // ILI9481 PVI35 variation + ILI9341_SLPOUT, 0x80, // Exit Sleep + 0xD0, 3, 0x07, 0x41, 0x1D, + 0xD1, 3, 0x00, 0x2b, 0x1f, + 0xD2, 2, 0x01, 0x11, + ILI9341_PWCTR1, 5, 0x10, 0x3B, 0x00, 0x02, 0x11, // Power control VRH[5:0] + ILI9341_VMCTR1, 1, 0x03, // VCM control + 0xC8, 12, 0x00, 0x14, 0x33, 0x10, 0x00, 0x16, 0x44, + 0x36, 0x77, 0x00, 0x0F, 0x00, + 0xB0, 1, 0x00, + 0xE4, 1, 0xA0, + 0xF0, 1, 0x01, + 0xF3, 2, 0x40, 0x0A, + ILI9341_MADCTL, 1, 0x0A, // Memory Access Control + ILI9341_PIXFMT, 1, 0x55, // Pixel format 0x55=16bit, + // 0x66=18bit + ILI9341_INVON, 0, + ILI9341_CASET, 4, 0x00, 0x00, 0x01, 0x3F, + ILI9341_PASET, 0x84, 0x00, 0x00, 0x01, 0xDF, + ILI9341_DISPON, 0x80, // Display on + 0x00 // End of list +}; + +// clang-format on + +// clang-format off +static const uint8_t PROGMEM initcmd_9481_AUO317[] = { // ILI9481 AUO317 variation + ILI9341_SLPOUT, 0x80, // Exit Sleep + 0xD0, 3, 0x07, 0x40, 0x1D, + 0xD1, 3, 0x00, 0x18, 0x13, + 0xD2, 2, 0x01, 0x11, + ILI9341_PWCTR1, 5, 0x10, 0x3B, 0x00, 0x02, 0x11, // Power control VRH[5:0] + ILI9341_VMCTR1, 1, 0x03, // VCM control + 0xC8, 12, 0x00, 0x44, 0x06, 0x44, 0x0A, 0x08, 0x17, + 0x33, 0x77, 0x44, 0x08, 0x0C, + 0xB0, 1, 0x00, + 0xE4, 1, 0xA0, + 0xF0, 1, 0x01, + ILI9341_MADCTL, 1, 0x0A, // Memory Access Control + ILI9341_PIXFMT, 1, 0x55, // Pixel format 0x55=16bit, + // 0x66=18bit + ILI9341_INVON, 0, + ILI9341_CASET, 4, 0x00, 0x00, 0x01, 0x3F, + ILI9341_PASET, 0x84, 0x00, 0x00, 0x01, 0xDF, + ILI9341_DISPON, 0x80, // Display on + 0x00 // End of list +}; + +// clang-format on + +// clang-format off +static const uint8_t PROGMEM initcmd_9481_CM35[] = { // ILI9481 CMO36 variation + ILI9341_SLPOUT, 0x80, // Exit Sleep + 0xD0, 3, 0x07, 0x41, 0x1D, + 0xD1, 3, 0x00, 0x1C, 0x1F, + 0xD2, 2, 0x01, 0x11, + ILI9341_PWCTR1, 5, 0x10, 0x3B, 0x00, 0x02, 0x11, // Power control VRH[5:0] + ILI9341_VMCTR1, 1, 0x03, // VCM control + 0xC6, 1, 0x83, + 0xC8, 12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, 0x65, + 0x23, 0x77, 0x00, 0x0F, 0x00, + 0xB0, 1, 0x00, + 0xE4, 1, 0xA0, + 0xF0, 1, 0x01, + ILI9341_MADCTL, 1, 0x0A, // Memory Access Control + ILI9341_PIXFMT, 1, 0x55, // Pixel format 0x55=16bit, 0x66=18bit + ILI9341_INVON, 0, + ILI9341_CASET, 4, 0x00, 0x00, 0x01, 0x3F, + ILI9341_PASET, 0x84, 0x00, 0x00, 0x01, 0xDF, + ILI9341_DISPON, 0x80, // Display on + 0x00 // End of list +}; + +// clang-format on + +// clang-format off +static const uint8_t PROGMEM initcmd_9481_RGB[] = { // ILI9481 RGB variation + ILI9341_SLPOUT, 0x80, // Exit Sleep + 0xD0, 3, 0x07, 0x41, 0x1D, + 0xD1, 3, 0x00, 0x2B, 0x1F, + 0xD2, 2, 0x01, 0x11, + ILI9341_PWCTR1, 6, 0x10, 0x3B, 0x00, 0x02, 0x11, + 0x00, // Power control VRH[5:0] + ILI9341_VMCTR1, 1, 0x03, // VCM control + 0xC6, 1, 0x80, + 0xC8, 12, 0x00, 0x14, 0x33, 0x10, 0x00, + 0x16, + 0x44, + 0x36, 0x77, 0x00, 0x0F, 0x00, + 0xB0, 1, 0x00, + 0xE4, 1, 0xA0, + 0xF0, 1, 0x08, + 0xF3, 2, 0x40, 0x0A, + 0xF6, 1, 0x84, + 0xF7, 1, 0x80, + ILI9341_FRMCTR3, 4, 0x00, 0x01, 0x06, 0x30, + ILI9341_INVCTR, 1, 0x00, + ILI9341_RDPIXFMT, 2, 0x00, 0x55, + ILI9341_MADCTL, 1, 0x0A, // Memory Access Control + ILI9341_PIXFMT, 1, 0x55, // Pixel format 0x55=16bit, 0x66=18bit + ILI9341_INVON, 0, + ILI9341_CASET, 4, 0x00, 0x00, 0x01, 0x3F, + ILI9341_PASET, 0x84, 0x00, 0x00, 0x01, 0xDF, + ILI9341_DISPON, 0x80, // Display on + 0x00 // End of list +}; + +// clang-format on + +// clang-format off +static const uint8_t PROGMEM initcmd_9481_CMI7[] = { // ILI9481 CMI7 (TFT_eSPI INIT_7) variation + ILI9341_SLPOUT, 0x80, // Exit Sleep + 0xD0, 3, 0x07, 0x42, 0x1B, + 0xD1, 3, 0x00, 0x14, 0x1B, + 0xD2, 2, 0x01, 0x12, + ILI9341_PWCTR1, 5, 0x10, 0x3B, 0x00, 0x02, 0x01, // Power control VRH[5:0] + ILI9341_VMCTR1, 1, 0x03, // VCM control + 0xC8, 12, 0x00, 0x46, 0x44, 0x50, 0x04, 0x16, 0x33, + 0x13, 0x77, 0x05, 0x0F, 0x00, + ILI9341_MADCTL, 1, 0x0A, // Memory Access Control + ILI9341_PIXFMT, 1, 0x55, // Pixel format + // 0x55=16bit, + // 0x66=18bit + ILI9341_INVON, 0, + 0x22, 4, 0x00, 0x00, 0x01, 0x3F, + ILI9341_PASET, 0x84, 0x00, 0x00, 0x01, 0xE0, + ILI9341_DISPON, 0x80, // Display on + 0x00 // End of list +}; + +// clang-format on + +// clang-format off +static const uint8_t PROGMEM initcmd_9481_CMI8[] = { // ILI9481 CMI8 (TFT_eSPI INIT_8) variation + ILI9341_SLPOUT, 0x80, // Exit Sleep + 0xD0, 0x83, 0x07, 0x44, 0x1E, + 0xD1, 3, 0x00, 0x0C, 0x1A, + ILI9341_VMCTR1, 1, 0x03, // VCM control + 0xD2, 2, 0x01, 0x11, + 0xE4, 1, 0xA0, + 0xF3, 2, 0x00, 0x2A, + 0xC8, 12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, + 0x65, + 0x23, 0x77, 0x00, 0x0F, 0x00, + ILI9341_PWCTR1, 5, 0x00, 0x3B, 0x00, 0x02, 0x11, // Power control VRH[5:0] + 0xC6, 1, 0x83, + 0xF0, 1, 0x01, + 0xE4, 1, 0xA0, + ILI9341_MADCTL, 1, 0x0A, // Memory Access Control + ILI9341_PIXFMT, 1, 0x55, // Pixel format 0x55=16bit, + // 0x66=18bit + ILI9341_INVCTR, 0x84, 0x02, 0x00, 0x00, 0x01, + ILI9341_CASET, 4, 0x00, 0x00, 0x01, 0x3F, + ILI9341_PASET, 4, 0x00, 0x00, 0x01, 0xDF, + ILI9341_DISPON, 0x80, // Display on + ILI9341_RAMWR, 0, + 0x00 // End of list +}; + +// clang-format on + +#ifdef ILI9341_ENABLE_ILI948X + +// clang-format off +static const uint8_t PROGMEM initcmd_9486[] = { // ILI9486 + ILI9341_SLPOUT, 0x80, // Exit Sleep + ILI9341_PIXFMT, 1, 0x55, // Pixel format 0x55=16bit, 0x66=18bit + ILI9341_PWCTR3, 1, 0x44, // Power control3 + ILI9341_VMCTR1, 4, 0x00, 0x00, 0x00, 0x00, // VCM control + ILI9341_GMCTRP1, 15, 0x0F, 0x1F, 0x1c, 0x0C, 0x0F, 0x08, 0x48, 0x98, 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00, + ILI9341_GMCTRN1, 15, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00, + ILI9341_INVOFF, 0, + ILI9341_MADCTL, 1, 0x48, // Memory Access Control + ILI9341_DISPON, 0x80, // Display on + 0x00 // End of list +}; + +// clang-format on + +// clang-format off +static const uint8_t PROGMEM initcmd_9488[] = { // ILI9488 + // Set gamma + ILI9341_GMCTRP1, 15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, + 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, + 0x0F, + + // Set gamma + ILI9341_GMCTRN1, 15, 0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, + 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, + 0x0F, + + // Power control VRH[5:0] + ILI9341_PWCTR1, 2, 0x17, 0x15, + + // Power control SAP[2:0];BT[3:0] + ILI9341_PWCTR2, 1, 0x41, + + // VCM control + ILI9341_VMCTR1, 3, 0x00, 0x12, 0x80, + + // Memory access Control + ILI9341_MADCTL, 1, 0x48, + + // Pixel format 0x55=16bit, 0x66=18bit + ILI9341_PIXFMT, 1, 0x55, + + // Interface control Mode + 0xB0, 1, 0x80, + + // Frame rate + ILI9341_FRMCTR1, 1, 0xA0, + + // Display onversion control + ILI9341_INVCTR, 1, 0x02, + + // Display function control + ILI9341_DFUNCTR, 2, 0x02, 0x02, + + // Disable 24 bit data + 0xE9, 1, 0x00, + + // Adjust control + 0xF7, 4, 0xA9, 0x51, 0x2C, 0x82, + + // Exit sleep + ILI9341_SLPOUT, 0x80, + + // Display on + ILI9341_DISPON, 0x80, + + // End of list + 0x00 +}; +#endif // ifdef ILI9341_ENABLE_ILI948X + +// clang-format on + +/**************************************************************************/ + +/*! + @brief Initialize ILI9341 chip + Connects to the ILI9341 over SPI and sends initialization procedure commands + @param freq Desired SPI clock frequency + */ + +/**************************************************************************/ +void Adafruit_ILI9341::begin(uint32_t freq) { + if (!freq) { + freq = SPI_DEFAULT_FREQ; + } + initSPI(freq); + + if (_rst < 0) { // If no hardware reset pin... + sendCommand(ILI9341_SWRESET); // Engage software reset + delay(150); + } + + uint8_t cmd, x, numArgs; + const uint8_t *addr; // = initcmd; + + switch (_model) { + case ILI_TYPE_9341: // ILI9341 + addr = initcmd; + break; + case ILI_TYPE_9342: // ILI9342 M5STACK + addr = initcmd_9342; + break; + case ILI_TYPE_9481: // ILI9481 + addr = initcmd_9481; + break; + case ILI_TYPE_9481_CPT29: // ILI9481 CPT29 + addr = initcmd_9481_CPT29; + break; + case ILI_TYPE_9481_PVI35: // ILI9481 PVI35 + addr = initcmd_9481_PVI35; + break; + case ILI_TYPE_9481_AUO317: // ILI9481 AUO317 + addr = initcmd_9481_AUO317; + break; + case ILI_TYPE_9481_CMO35: // ILI9481 CMO35 + addr = initcmd_9481_CM35; + break; + case ILI_TYPE_9481_RGB: // ILI9481 RGB + addr = initcmd_9481_RGB; + break; + case ILI_TYPE_9481_CMI7: // ILI9481 CMI7 + addr = initcmd_9481_CMI7; + break; + case ILI_TYPE_9481_CMI8: // ILI9481 CMI8 + addr = initcmd_9481_CMI8; + break; + #ifdef ILI9341_ENABLE_ILI948X + case ILI_TYPE_9486: // ILI9486 + addr = initcmd_9486; + break; + case ILI_TYPE_9488: // ILI9488 + addr = initcmd_9488; + break; + #endif // ifdef ILI9341_ENABLE_ILI948X + default: + addr = initcmd; + break; + } + + while ((cmd = pgm_read_byte(addr++)) > 0) { + x = pgm_read_byte(addr++); + numArgs = x & 0x7F; + sendCommand(cmd, addr, numArgs); + addr += numArgs; + + if (x & 0x80) { + delay(150); + } + } + + _width = _w; + _height = _h; +} + +/**************************************************************************/ + +/*! + @brief Set origin of (0,0) and orientation of TFT display + @param m The index for rotation, from 0-3 inclusive + */ + +/**************************************************************************/ +void Adafruit_ILI9341::setRotation(uint8_t m) { + rotation = m % 4; // can't be higher than 3 + + switch (rotation) { + case 0: + + switch (_model) { + case ILI_TYPE_9342: // ILI9342 M5STACK + m = (MADCTL_MY | MADCTL_MV | MADCTL_BGR); + break; + case ILI_TYPE_9481: // ILI9481 + case ILI_TYPE_9481_CPT29: // ILI9481 CPT29 + case ILI_TYPE_9481_PVI35: // ILI9481 PVI35 + case ILI_TYPE_9481_AUO317: // ILI9481 AUO317 + case ILI_TYPE_9481_CMO35: // ILI9481 CMO35 + case ILI_TYPE_9481_RGB: // ILI9481 RGB + case ILI_TYPE_9481_CMI7: // ILI9481 CMI7 + case ILI_TYPE_9481_CMI8: // ILI9481 CMI8 + m = (MADCTL_SS | MADCTL_BGR); + break; + case ILI_TYPE_9341: // ILI9341 + // m = (MADCTL_MX | MADCTL_BGR); + // break; + #ifdef ILI9341_ENABLE_ILI948X + case ILI_TYPE_9486: // ILI9486 + // m = (MADCTL_MX | MADCTL_BGR); + // break; + case ILI_TYPE_9488: // ILI9488 + // m = (MADCTL_MX | MADCTL_BGR); + // break; + #endif // ifdef ILI9341_ENABLE_ILI948X + default: + m = (MADCTL_MX | MADCTL_BGR); + break; + } + _width = _w; + _height = _h; + break; + case 1: + + switch (_model) { + case ILI_TYPE_9342: // ILI9342 M5STACK + m = (MADCTL_BGR); + break; + case ILI_TYPE_9481: // ILI9481 + case ILI_TYPE_9481_CPT29: // ILI9481 CPT29 + case ILI_TYPE_9481_PVI35: // ILI9481 PVI35 + case ILI_TYPE_9481_AUO317: // ILI9481 AUO317 + case ILI_TYPE_9481_CMO35: // ILI9481 CMO35 + case ILI_TYPE_9481_RGB: // ILI9481 RGB + case ILI_TYPE_9481_CMI7: // ILI9481 CMI7 + case ILI_TYPE_9481_CMI8: // ILI9481 CMI8 + // m = (MADCTL_MV | MADCTL_BGR); + // break; + case ILI_TYPE_9341: // ILI9341 + // m = (MADCTL_MV | MADCTL_BGR); + // break; + #ifdef ILI9341_ENABLE_ILI948X + case ILI_TYPE_9486: // ILI9486 + // m = (MADCTL_MV | MADCTL_BGR); + // break; + case ILI_TYPE_9488: // ILI9488 + // m = (MADCTL_MV | MADCTL_BGR); + // break; + #endif // ifdef ILI9341_ENABLE_ILI948X + default: + m = (MADCTL_MV | MADCTL_BGR); + break; + } + _width = _h; + _height = _w; + break; + case 2: + + switch (_model) { + case ILI_TYPE_9342: // ILI9342 M5STACK + m = (MADCTL_MV | MADCTL_MX | MADCTL_BGR); + break; + case ILI_TYPE_9481: // ILI9481 + case ILI_TYPE_9481_CPT29: // ILI9481 CPT29 + case ILI_TYPE_9481_PVI35: // ILI9481 PVI35 + case ILI_TYPE_9481_AUO317: // ILI9481 AUO317 + case ILI_TYPE_9481_CMO35: // ILI9481 CMO35 + case ILI_TYPE_9481_RGB: // ILI9481 RGB + case ILI_TYPE_9481_CMI7: // ILI9481 CMI7 + case ILI_TYPE_9481_CMI8: // ILI9481 CMI8 + m = (MADCTL_GS | MADCTL_BGR); + break; + case ILI_TYPE_9341: // ILI9341 + // m = (MADCTL_MY | MADCTL_BGR); + // break; + #ifdef ILI9341_ENABLE_ILI948X + case ILI_TYPE_9486: // ILI9486 + // m = (MADCTL_MY | MADCTL_BGR); + // break; + case ILI_TYPE_9488: // ILI9488 + // m = (MADCTL_MY | MADCTL_BGR); + // break; + #endif // ifdef ILI9341_ENABLE_ILI948X + default: + m = (MADCTL_MY | MADCTL_BGR); + break; + } + _width = _w; + _height = _h; + break; + case 3: + + switch (_model) { + case ILI_TYPE_9342: // ILI9342 M5STACK + m = (MADCTL_MX | MADCTL_MY | MADCTL_BGR); + break; + case ILI_TYPE_9481: // ILI9481 + case ILI_TYPE_9481_CPT29: // ILI9481 CPT29 + case ILI_TYPE_9481_PVI35: // ILI9481 PVI35 + case ILI_TYPE_9481_AUO317: // ILI9481 AUO317 + case ILI_TYPE_9481_CMO35: // ILI9481 CMO35 + case ILI_TYPE_9481_RGB: // ILI9481 RGB + case ILI_TYPE_9481_CMI7: // ILI9481 CMI7 + case ILI_TYPE_9481_CMI8: // ILI9481 CMI8 + m = (MADCTL_SS | MADCTL_GS | MADCTL_MV | MADCTL_BGR); + break; + case ILI_TYPE_9341: // ILI9341 + // m = (MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); + // break; + #ifdef ILI9341_ENABLE_ILI948X + case ILI_TYPE_9486: // ILI9486 + // m = (MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); + // break; + case ILI_TYPE_9488: // ILI9488 + // m = (MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); + // break; + #endif // ifdef ILI9341_ENABLE_ILI948X + default: + m = (MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); + break; + } + _width = _h; + _height = _w; + break; + } + + sendCommand(ILI9341_MADCTL, &m, 1); +} + +/**************************************************************************/ + +/*! + @brief Enable/Disable display color inversion + @param invert True to invert, False to have normal color + */ + +/**************************************************************************/ +void Adafruit_ILI9341::invertDisplay(bool invert) { + sendCommand(invert ? ILI9341_INVON : ILI9341_INVOFF); +} + +/**************************************************************************/ + +/*! + @brief Scroll display memory + @param y How many pixels to scroll display by + */ + +/**************************************************************************/ +void Adafruit_ILI9341::scrollTo(uint16_t y) { + uint8_t data[2]; + + data[0] = y >> 8; + data[1] = y & 0xff; + sendCommand(ILI9341_VSCRSADD, (uint8_t *)data, 2); +} + +/**************************************************************************/ + +/*! + @brief Set the height of the Top and Bottom Scroll Margins + @param top The height of the Top scroll margin + @param bottom The height of the Bottom scroll margin + */ + +/**************************************************************************/ +void Adafruit_ILI9341::setScrollMargins(uint16_t top, uint16_t bottom) { + // TFA+VSA+BFA must equal 320 + if (top + bottom <= ILI9341_TFTHEIGHT) { + uint16_t middle = ILI9341_TFTHEIGHT - (top + bottom); + uint8_t data[6]; + data[0] = top >> 8; + data[1] = top & 0xff; + data[2] = middle >> 8; + data[3] = middle & 0xff; + data[4] = bottom >> 8; + data[5] = bottom & 0xff; + sendCommand(ILI9341_VSCRDEF, (uint8_t *)data, 6); + } +} + +/**************************************************************************/ + +/*! + @brief Set the "address window" - the rectangle we will write to RAM with + the next chunk of SPI data writes. The ILI9341 will automatically wrap + the data as each row is filled + @param x1 TFT memory 'x' origin + @param y1 TFT memory 'y' origin + @param w Width of rectangle + @param h Height of rectangle + */ + +/**************************************************************************/ +void Adafruit_ILI9341::setAddrWindow(uint16_t x1, uint16_t y1, uint16_t w, + uint16_t h) { + uint16_t x2 = (x1 + w - 1), y2 = (y1 + h - 1); + + writeCommand(ILI9341_CASET); // Column address set + SPI_WRITE16(x1); + SPI_WRITE16(x2); + writeCommand(ILI9341_PASET); // Row address set + SPI_WRITE16(y1); + SPI_WRITE16(y2); + writeCommand(ILI9341_RAMWR); // Write to RAM +} + +/**************************************************************************/ + +/*! + @brief Read 8 bits of data from ILI9341 configuration memory. NOT from RAM! + This is highly undocumented/supported, it's really a hack but kinda + works? + @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 ILI9341 register + */ + +/**************************************************************************/ +uint8_t Adafruit_ILI9341::readcommand8(uint8_t commandByte, uint8_t index) { + uint8_t data = 0x10 + index; + + sendCommand(0xD9, &data, 1); // Set Index Register + return Adafruit_SPITFT::readcommand8(commandByte); +} diff --git a/lib/Adafruit_ILI9341/Adafruit_ILI9341.h b/lib/Adafruit_ILI9341/Adafruit_ILI9341.h new file mode 100644 index 000000000..81fc609b3 --- /dev/null +++ b/lib/Adafruit_ILI9341/Adafruit_ILI9341.h @@ -0,0 +1,198 @@ +/*! + * @file Adafruit_ILI9341.h + * + * This is the documentation for Adafruit's ILI9341 driver for the + * Arduino platform. + * + * This library works with the Adafruit 2.8" Touch Shield V2 (SPI) + * http://www.adafruit.com/products/1651 + * Adafruit 2.4" TFT LCD with Touchscreen Breakout w/MicroSD Socket - ILI9341 + * https://www.adafruit.com/product/2478 + * 2.8" TFT LCD with Touchscreen Breakout Board w/MicroSD Socket - ILI9341 + * https://www.adafruit.com/product/1770 + * 2.2" 18-bit color TFT LCD display with microSD card breakout - ILI9340 + * https://www.adafruit.com/product/1770 + * TFT FeatherWing - 2.4" 320x240 Touchscreen For All Feathers + * https://www.adafruit.com/product/3315 + * + * 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! + * + * + * This library depends on + * Adafruit_GFX being present on your system. Please make sure you have + * installed the latest version before using this library. + * + * Written by Limor "ladyada" Fried for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#ifndef _ADAFRUIT_ILI9341H_ +#define _ADAFRUIT_ILI9341H_ + +#include "Adafruit_GFX.h" +#include "Arduino.h" +#include "Print.h" +#include +#include +#include + +// #define ILI9341_ENABLE_ILI948X ///< Enable ILI9486 and ILI9488 support, MUST reflect a similar #define in P095_data_struct.h ! + +#define ILI9341_TFTWIDTH 240 ///< ILI9341 max TFT width +#define ILI9341_TFTHEIGHT 320 ///< ILI9341 max TFT height + +#define ILI9341_NOP 0x00 ///< No-op register +#define ILI9341_SWRESET 0x01 ///< Software reset register +#define ILI9341_RDDID 0x04 ///< Read display identification information +#define ILI9341_RDDST 0x09 ///< Read Display Status + +#define ILI9341_SLPIN 0x10 ///< Enter Sleep Mode +#define ILI9341_SLPOUT 0x11 ///< Sleep Out +#define ILI9341_PTLON 0x12 ///< Partial Mode ON +#define ILI9341_NORON 0x13 ///< Normal Display Mode ON + +#define ILI9341_RDMODE 0x0A ///< Read Display Power Mode +#define ILI9341_RDMADCTL 0x0B ///< Read Display MADCTL +#define ILI9341_RDPIXFMT 0x0C ///< Read Display Pixel Format +#define ILI9341_RDIMGFMT 0x0D ///< Read Display Image Format +#define ILI9341_RDSELFDIAG 0x0F ///< Read Display Self-Diagnostic Result + +#define ILI9341_INVOFF 0x20 ///< Display Inversion OFF +#define ILI9341_INVON 0x21 ///< Display Inversion ON +#define ILI9341_GAMMASET 0x26 ///< Gamma Set +#define ILI9341_DISPOFF 0x28 ///< Display OFF +#define ILI9341_DISPON 0x29 ///< Display ON + +#define ILI9341_CASET 0x2A ///< Column Address Set +#define ILI9341_PASET 0x2B ///< Page Address Set +#define ILI9341_RAMWR 0x2C ///< Memory Write +#define ILI9341_RAMRD 0x2E ///< Memory Read + +#define ILI9341_PTLAR 0x30 ///< Partial Area +#define ILI9341_VSCRDEF 0x33 ///< Vertical Scrolling Definition +#define ILI9341_MADCTL 0x36 ///< Memory Access Control +#define ILI9341_VSCRSADD 0x37 ///< Vertical Scrolling Start Address +#define ILI9341_PIXFMT 0x3A ///< COLMOD: Pixel Format Set + +#define ILI9341_FRMCTR1 \ + 0xB1 ///< Frame Rate Control (In Normal Mode/Full Colors) +#define ILI9341_FRMCTR2 0xB2 ///< Frame Rate Control (In Idle Mode/8 colors) +#define ILI9341_FRMCTR3 \ + 0xB3 ///< Frame Rate control (In Partial Mode/Full Colors) +#define ILI9341_INVCTR 0xB4 ///< Display Inversion Control +#define ILI9341_DFUNCTR 0xB6 ///< Display Function Control + +#define ILI9341_PWCTR1 0xC0 ///< Power Control 1 +#define ILI9341_PWCTR2 0xC1 ///< Power Control 2 +#define ILI9341_PWCTR3 0xC2 ///< Power Control 3 +#define ILI9341_PWCTR4 0xC3 ///< Power Control 4 +#define ILI9341_PWCTR5 0xC4 ///< Power Control 5 +#define ILI9341_VMCTR1 0xC5 ///< VCOM Control 1 +#define ILI9341_VMCTR2 0xC7 ///< VCOM Control 2 + +#define ILI9341_RDID1 0xDA ///< Read ID 1 +#define ILI9341_RDID2 0xDB ///< Read ID 2 +#define ILI9341_RDID3 0xDC ///< Read ID 3 +#define ILI9341_RDID4 0xDD ///< Read ID 4 + +#define ILI9341_GMCTRP1 0xE0 ///< Positive Gamma Correction +#define ILI9341_GMCTRN1 0xE1 ///< Negative Gamma Correction +// #define ILI9341_PWCTR6 0xFC + +// Color definitions +#define ILI9341_BLACK 0x0000 ///< 0, 0, 0 +#define ILI9341_NAVY 0x000F ///< 0, 0, 123 +#define ILI9341_DARKGREEN 0x03E0 ///< 0, 125, 0 +#define ILI9341_DARKCYAN 0x03EF ///< 0, 125, 123 +#define ILI9341_MAROON 0x7800 ///< 123, 0, 0 +#define ILI9341_PURPLE 0x780F ///< 123, 0, 123 +#define ILI9341_OLIVE 0x7BE0 ///< 123, 125, 0 +#define ILI9341_LIGHTGREY 0xC618 ///< 198, 195, 198 +#define ILI9341_DARKGREY 0x7BEF ///< 123, 125, 123 +#define ILI9341_BLUE 0x001F ///< 0, 0, 255 +#define ILI9341_GREEN 0x07E0 ///< 0, 255, 0 +#define ILI9341_CYAN 0x07FF ///< 0, 255, 255 +#define ILI9341_RED 0xF800 ///< 255, 0, 0 +#define ILI9341_MAGENTA 0xF81F ///< 255, 0, 255 +#define ILI9341_YELLOW 0xFFE0 ///< 255, 255, 0 +#define ILI9341_WHITE 0xFFFF ///< 255, 255, 255 +#define ILI9341_ORANGE 0xFD20 ///< 255, 165, 0 +#define ILI9341_GREENYELLOW 0xAFE5 ///< 173, 255, 41 +#define ILI9341_PINK 0xFC18 ///< 255, 130, 198 + + +#define ILI_TYPE_9341 0 // MUST match with enum class ILI9xxx_type_e in P095_data_struct.h ! +#define ILI_TYPE_9342 1 +#define ILI_TYPE_9481 2 +#define ILI_TYPE_9481_CPT29 3 +#define ILI_TYPE_9481_PVI35 4 +#define ILI_TYPE_9481_AUO317 5 +#define ILI_TYPE_9481_CMO35 6 +#define ILI_TYPE_9481_RGB 7 +#define ILI_TYPE_9481_CMI7 8 +#define ILI_TYPE_9481_CMI8 9 +#ifdef ILI9341_ENABLE_ILI948X +# define ILI_TYPE_9486 10 +# define ILI_TYPE_9488 11 +#endif // ifndef ILI9341_ENABLE_ILI948X + +/**************************************************************************/ + +/*! + @brief Class to manage hardware interface with ILI9341 chipset (also seems to + work with ILI9340) + */ + +/**************************************************************************/ + +class Adafruit_ILI9341 : public Adafruit_SPITFT { +public: + + // Adafruit_ILI9341(int8_t _CS, int8_t _DC, int8_t _MOSI, int8_t _SCLK, + // int8_t _RST = -1, int8_t _MISO = -1); + Adafruit_ILI9341(int8_t _CS, + int8_t _DC, + int8_t _RST = -1, + uint8_t _MODEL = ILI_TYPE_9341, + uint16_t w = ILI9341_TFTWIDTH, + uint16_t h = ILI9341_TFTHEIGHT); // Default to ILI9341, check ILI9xxx_type_e + virtual ~Adafruit_ILI9341() {} // Virtual destructor + +#if !defined(ESP8266) + + // Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs = -1, + // int8_t rst = -1); +#endif // end !ESP8266 + // Adafruit_ILI9341(tftBusWidth busWidth, int8_t d0, int8_t wr, int8_t dc, + // int8_t cs = -1, int8_t rst = -1, int8_t rd = -1); + void begin(uint32_t freq = 0); + void setRotation(uint8_t r); + void invertDisplay(bool i); + void scrollTo(uint16_t y); + void setScrollMargins(uint16_t top, + uint16_t bottom); + + // Transaction API not used by GFX + void setAddrWindow(uint16_t x, + uint16_t y, + uint16_t w, + uint16_t h); + + uint8_t readcommand8(uint8_t reg, + uint8_t index = 0); + +private: + + uint16_t _w; + uint16_t _h; + uint8_t _model; +}; + +#endif // _ADAFRUIT_ILI9341H_ diff --git a/lib/Adafruit_ILI9341/README.md b/lib/Adafruit_ILI9341/README.md new file mode 100644 index 000000000..2f62bcd11 --- /dev/null +++ b/lib/Adafruit_ILI9341/README.md @@ -0,0 +1,38 @@ +# Adafruit ILI9341 Arduino Library [![Build Status](https://github.com/adafruit/Adafruit_ILI9341/workflows/Arduino%20Library%20CI/badge.svg)](https://github.com/adafruit/Adafruit_ILI9341/actions)[![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit_ILI9341/html/index.html) + +This is a library for the Adafruit ILI9341 display products + +This library works with the Adafruit 2.8" Touch Shield V2 (SPI) + * http://www.adafruit.com/products/1651 + +Adafruit 2.4" TFT LCD with Touchscreen Breakout w/MicroSD Socket - ILI9341 + * https://www.adafruit.com/product/2478 + +2.8" TFT LCD with Touchscreen Breakout Board w/MicroSD Socket - ILI9341 + * https://www.adafruit.com/product/1770 + +2.2" 18-bit color TFT LCD display with microSD card breakout - ILI9340 + * https://www.adafruit.com/product/1480 + +TFT FeatherWing - 2.4" 320x240 Touchscreen For All Feathers + * https://www.adafruit.com/product/3315 + +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). + +**BMP image-loading examples have been moved to the Adafruit_ImageReader library:** +https://github.com/adafruit/Adafruit_ImageReader + +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 + +To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder Adafruit_ILI9341. Check that the Adafruit_ILI9341 folder contains Adafruit_ILI9341.cpp and Adafruit_ILI9341. + +Place the Adafruit_ILI9341 library folder your arduinosketchfolder/libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE + +Also requires the [Adafruit_GFX](https://github.com/adafruit/Adafruit-GFX-Library) library for Arduino. diff --git a/lib/Adafruit_ILI9341/examples/breakouttouchpaint/.esp8266.test.skip b/lib/Adafruit_ILI9341/examples/breakouttouchpaint/.esp8266.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/Adafruit_ILI9341/examples/breakouttouchpaint/breakouttouchpaint.ino b/lib/Adafruit_ILI9341/examples/breakouttouchpaint/breakouttouchpaint.ino new file mode 100644 index 000000000..9022f2419 --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/breakouttouchpaint/breakouttouchpaint.ino @@ -0,0 +1,145 @@ +/*************************************************** + This is our touchscreen painting example for the Adafruit ILI9341 Breakout + ----> http://www.adafruit.com/products/1770 + + 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 + ****************************************************/ + +/** NOT FOR USE WITH THE TOUCH SHIELD, ONLY FOR THE BREAKOUT! **/ + +#include // Core graphics library +#include +#include +#include "TouchScreen.h" + +// These are the four touchscreen analog pins +#define YP A2 // must be an analog pin, use "An" notation! +#define XM A3 // must be an analog pin, use "An" notation! +#define YM 9 // can be any digital pin +#define XP 8 // can be any digital pin + +// This is calibration data for the raw touch data to the screen coordinates +#define TS_MINX 150 +#define TS_MINY 120 +#define TS_MAXX 920 +#define TS_MAXY 940 + +#define MINPRESSURE 10 +#define MAXPRESSURE 1000 + +// The display uses hardware SPI, plus #9 & #10 +#define TFT_CS 10 +#define TFT_DC 9 +Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); + +// For better pressure precision, we need to know the resistance +// between X+ and X- Use any multimeter to read it +// For the one we're using, its 300 ohms across the X plate +TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); + +// Size of the color selection boxes and the paintbrush size +#define BOXSIZE 40 +#define PENRADIUS 3 +int oldcolor, currentcolor; + +void setup(void) { + // while (!Serial); // used for leonardo debugging + + Serial.begin(9600); + Serial.println(F("Touch Paint!")); + + tft.begin(); + tft.fillScreen(ILI9341_BLACK); + + // make the color selection boxes + tft.fillRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_RED); + tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, ILI9341_YELLOW); + tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, ILI9341_GREEN); + tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, ILI9341_CYAN); + tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, ILI9341_BLUE); + tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, ILI9341_MAGENTA); + + // select the current color 'red' + tft.drawRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + currentcolor = ILI9341_RED; +} + + +void loop() +{ + // Retrieve a point + TSPoint p = ts.getPoint(); + + /* + Serial.print("X = "); Serial.print(p.x); + Serial.print("\tY = "); Serial.print(p.y); + Serial.print("\tPressure = "); Serial.println(p.z); + */ + + // we have some minimum pressure we consider 'valid' + // pressure of 0 means no pressing! + if (p.z < MINPRESSURE || p.z > MAXPRESSURE) { + return; + } + + // Scale from ~0->1000 to tft.width using the calibration #'s + p.x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width()); + p.y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height()); + + /* + Serial.print("("); Serial.print(p.x); + Serial.print(", "); Serial.print(p.y); + Serial.println(")"); + */ + + + if (p.y < BOXSIZE) { + oldcolor = currentcolor; + + if (p.x < BOXSIZE) { + currentcolor = ILI9341_RED; + tft.drawRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*2) { + currentcolor = ILI9341_YELLOW; + tft.drawRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*3) { + currentcolor = ILI9341_GREEN; + tft.drawRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*4) { + currentcolor = ILI9341_CYAN; + tft.drawRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*5) { + currentcolor = ILI9341_BLUE; + tft.drawRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*6) { + currentcolor = ILI9341_MAGENTA; + tft.drawRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } + + if (oldcolor != currentcolor) { + if (oldcolor == ILI9341_RED) + tft.fillRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_RED); + if (oldcolor == ILI9341_YELLOW) + tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, ILI9341_YELLOW); + if (oldcolor == ILI9341_GREEN) + tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, ILI9341_GREEN); + if (oldcolor == ILI9341_CYAN) + tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, ILI9341_CYAN); + if (oldcolor == ILI9341_BLUE) + tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, ILI9341_BLUE); + if (oldcolor == ILI9341_MAGENTA) + tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, ILI9341_MAGENTA); + } + } + if (((p.y-PENRADIUS) > BOXSIZE) && ((p.y+PENRADIUS) < tft.height())) { + tft.fillCircle(p.x, p.y, PENRADIUS, currentcolor); + } +} diff --git a/lib/Adafruit_ILI9341/examples/graphicstest/graphicstest.ino b/lib/Adafruit_ILI9341/examples/graphicstest/graphicstest.ino new file mode 100644 index 000000000..3154d4095 --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/graphicstest/graphicstest.ino @@ -0,0 +1,365 @@ +/*************************************************** + This is our GFX example for the Adafruit ILI9341 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 "Adafruit_GFX.h" +#include "Adafruit_ILI9341.h" + +// For the Adafruit shield, these are the default. +#define TFT_DC 9 +#define TFT_CS 10 + +// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC +Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); +// If using the breakout, change pins as desired +//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO); + +void setup() { + Serial.begin(9600); + Serial.println("ILI9341 Test!"); + + tft.begin(); + + // read diagnostics (optional but can help debug problems) + uint8_t x = tft.readcommand8(ILI9341_RDMODE); + Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDMADCTL); + Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDPIXFMT); + Serial.print("Pixel Format: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDIMGFMT); + Serial.print("Image Format: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDSELFDIAG); + Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); + + Serial.println(F("Benchmark Time (microseconds)")); + delay(10); + 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(ILI9341_CYAN)); + delay(500); + + Serial.print(F("Horiz/Vert Lines ")); + Serial.println(testFastLines(ILI9341_RED, ILI9341_BLUE)); + delay(500); + + Serial.print(F("Rectangles (outline) ")); + Serial.println(testRects(ILI9341_GREEN)); + delay(500); + + Serial.print(F("Rectangles (filled) ")); + Serial.println(testFilledRects(ILI9341_YELLOW, ILI9341_MAGENTA)); + delay(500); + + Serial.print(F("Circles (filled) ")); + Serial.println(testFilledCircles(10, ILI9341_MAGENTA)); + + Serial.print(F("Circles (outline) ")); + Serial.println(testCircles(10, ILI9341_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(ILI9341_BLACK); + yield(); + tft.fillScreen(ILI9341_RED); + yield(); + tft.fillScreen(ILI9341_GREEN); + yield(); + tft.fillScreen(ILI9341_BLUE); + yield(); + tft.fillScreen(ILI9341_BLACK); + yield(); + return micros() - start; +} + +unsigned long testText() { + tft.fillScreen(ILI9341_BLACK); + unsigned long start = micros(); + tft.setCursor(0, 0); + tft.setTextColor(ILI9341_WHITE); tft.setTextSize(1); + tft.println("Hello World!"); + tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(2); + tft.println(1234.56); + tft.setTextColor(ILI9341_RED); tft.setTextSize(3); + tft.println(0xDEADBEEF, HEX); + tft.println(); + tft.setTextColor(ILI9341_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(ILI9341_BLACK); + yield(); + + 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); + yield(); + } + + 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(ILI9341_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*10, i*10)); + t += micros() - start; + tft.drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i, + tft.color565(i*10, i*10, 0)); + yield(); + } + + return t; +} + +unsigned long testRoundRects() { + unsigned long start; + int w, i, i2, + cx = tft.width() / 2 - 1, + cy = tft.height() / 2 - 1; + + tft.fillScreen(ILI9341_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)); + yield(); + } + + return micros() - start; +} \ No newline at end of file diff --git a/lib/Adafruit_ILI9341/examples/graphicstest_featherwing/graphicstest_featherwing.ino b/lib/Adafruit_ILI9341/examples/graphicstest_featherwing/graphicstest_featherwing.ino new file mode 100644 index 000000000..190798772 --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/graphicstest_featherwing/graphicstest_featherwing.ino @@ -0,0 +1,397 @@ +/*************************************************** + This is our GFX example for the Adafruit ILI9341 TFT FeatherWing + ----> http://www.adafruit.com/products/3315 + + Check out the links above for our tutorials and wiring diagrams + + 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 +#include + +#ifdef ESP8266 + #define STMPE_CS 16 + #define TFT_CS 0 + #define TFT_DC 15 + #define SD_CS 2 +#elif defined(ESP32) && !defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) + #define STMPE_CS 32 + #define TFT_CS 15 + #define TFT_DC 33 + #define SD_CS 14 +#elif defined(TEENSYDUINO) + #define TFT_DC 10 + #define TFT_CS 4 + #define STMPE_CS 3 + #define SD_CS 8 +#elif defined(ARDUINO_STM32_FEATHER) + #define TFT_DC PB4 + #define TFT_CS PA15 + #define STMPE_CS PC7 + #define SD_CS PC5 +#elif defined(ARDUINO_NRF52832_FEATHER) /* BSP 0.6.5 and higher! */ + #define TFT_DC 11 + #define TFT_CS 31 + #define STMPE_CS 30 + #define SD_CS 27 +#elif defined(ARDUINO_MAX32620FTHR) || defined(ARDUINO_MAX32630FTHR) + #define TFT_DC P5_4 + #define TFT_CS P5_3 + #define STMPE_CS P3_3 + #define SD_CS P3_2 +#else + // Anything else, defaults! + #define STMPE_CS 6 + #define TFT_CS 9 + #define TFT_DC 10 + #define SD_CS 5 +#endif + + +Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); + +void setup() { + Serial.begin(115200); + + delay(10); + Serial.println("FeatherWing TFT Test!"); + + tft.begin(); + + // read diagnostics (optional but can help debug problems) + uint8_t x = tft.readcommand8(ILI9341_RDMODE); + Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDMADCTL); + Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDPIXFMT); + Serial.print("Pixel Format: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDIMGFMT); + Serial.print("Image Format: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDSELFDIAG); + Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); + + Serial.println(F("Benchmark Time (microseconds)")); + delay(10); + 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(ILI9341_CYAN)); + delay(500); + + Serial.print(F("Horiz/Vert Lines ")); + Serial.println(testFastLines(ILI9341_RED, ILI9341_BLUE)); + delay(500); + + Serial.print(F("Rectangles (outline) ")); + Serial.println(testRects(ILI9341_GREEN)); + delay(500); + + Serial.print(F("Rectangles (filled) ")); + Serial.println(testFilledRects(ILI9341_YELLOW, ILI9341_MAGENTA)); + delay(500); + + Serial.print(F("Circles (filled) ")); + Serial.println(testFilledCircles(10, ILI9341_MAGENTA)); + + Serial.print(F("Circles (outline) ")); + Serial.println(testCircles(10, ILI9341_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(ILI9341_BLACK); + yield(); + tft.fillScreen(ILI9341_RED); + yield(); + tft.fillScreen(ILI9341_GREEN); + yield(); + tft.fillScreen(ILI9341_BLUE); + yield(); + tft.fillScreen(ILI9341_BLACK); + yield(); + return micros() - start; +} + +unsigned long testText() { + tft.fillScreen(ILI9341_BLACK); + unsigned long start = micros(); + tft.setCursor(0, 0); + tft.setTextColor(ILI9341_WHITE); tft.setTextSize(1); + tft.println("Hello World!"); + tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(2); + tft.println(1234.56); + tft.setTextColor(ILI9341_RED); tft.setTextSize(3); + tft.println(0xDEADBEEF, HEX); + tft.println(); + tft.setTextColor(ILI9341_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(ILI9341_BLACK); + yield(); + + 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); + yield(); + } + + 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(ILI9341_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*10, i*10)); + t += micros() - start; + tft.drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i, + tft.color565(i*10, i*10, 0)); + yield(); + } + + return t; +} + +unsigned long testRoundRects() { + unsigned long start; + int w, i, i2, + cx = tft.width() / 2 - 1, + cy = tft.height() / 2 - 1; + + tft.fillScreen(ILI9341_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)); + yield(); + } + + return micros() - start; +} \ No newline at end of file diff --git a/lib/Adafruit_ILI9341/examples/graphicstest_pyportal/graphicstest_pyportal.ino b/lib/Adafruit_ILI9341/examples/graphicstest_pyportal/graphicstest_pyportal.ino new file mode 100644 index 000000000..faaddfdd5 --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/graphicstest_pyportal/graphicstest_pyportal.ino @@ -0,0 +1,360 @@ +/*************************************************** + GFX example for the Adafruit PyPortal. + https://www.adafruit.com/product/4116 + ****************************************************/ + +#include "Adafruit_GFX.h" +#include "Adafruit_ILI9341.h" + +#define TFT_D0 34 // Data bit 0 pin (MUST be on PORT byte boundary) +#define TFT_WR 26 // Write-strobe pin (CCL-inverted timer output) +#define TFT_DC 10 // Data/command pin +#define TFT_CS 11 // Chip-select pin +#define TFT_RST 24 // Reset pin +#define TFT_RD 9 // Read-strobe pin +#define TFT_BACKLIGHT 25 + +// ILI9341 with 8-bit parallel interface: +Adafruit_ILI9341 tft = Adafruit_ILI9341(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST, TFT_RD); + +void setup() { + Serial.begin(9600); + //while(!Serial); + Serial.println("ILI9341 Test!"); + + // Turn on backlight (required on PyPortal) + pinMode(TFT_BACKLIGHT, OUTPUT); + digitalWrite(TFT_BACKLIGHT, HIGH); + + tft.begin(); + + // read diagnostics (optional but can help debug problems) + uint8_t x = tft.readcommand8(ILI9341_RDMODE); + Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDMADCTL); + Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDPIXFMT); + Serial.print("Pixel Format: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDIMGFMT); + Serial.print("Image Format: 0x"); Serial.println(x, HEX); + x = tft.readcommand8(ILI9341_RDSELFDIAG); + Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); + + Serial.println(F("Benchmark Time (microseconds)")); + delay(10); + 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(ILI9341_CYAN)); + delay(500); + + Serial.print(F("Horiz/Vert Lines ")); + Serial.println(testFastLines(ILI9341_RED, ILI9341_BLUE)); + delay(500); + + Serial.print(F("Rectangles (outline) ")); + Serial.println(testRects(ILI9341_GREEN)); + delay(500); + + Serial.print(F("Rectangles (filled) ")); + Serial.println(testFilledRects(ILI9341_YELLOW, ILI9341_MAGENTA)); + delay(500); + + Serial.print(F("Circles (filled) ")); + Serial.println(testFilledCircles(10, ILI9341_MAGENTA)); + + Serial.print(F("Circles (outline) ")); + Serial.println(testCircles(10, ILI9341_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(ILI9341_BLACK); + yield(); + tft.fillScreen(ILI9341_RED); + yield(); + tft.fillScreen(ILI9341_GREEN); + yield(); + tft.fillScreen(ILI9341_BLUE); + yield(); + tft.fillScreen(ILI9341_BLACK); + yield(); + return micros() - start; +} + +unsigned long testText() { + tft.fillScreen(ILI9341_BLACK); + unsigned long start = micros(); + tft.setCursor(0, 0); + tft.setTextColor(ILI9341_WHITE); tft.setTextSize(1); + tft.println(F("Hello World!")); + tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(2); + tft.println(1234.56); + tft.setTextColor(ILI9341_RED); tft.setTextSize(3); + tft.println(0xDEADBEEF, HEX); + tft.println(); + tft.setTextColor(ILI9341_GREEN); + tft.setTextSize(5); + tft.println(F("Groop")); + tft.setTextSize(2); + tft.println(F("I implore thee,")); + tft.setTextSize(1); + tft.println(F("my foonting turlingdromes.")); + tft.println(F("And hooptiously drangle me")); + tft.println(F("with crinkly bindlewurdles,")); + tft.println(F("Or I will rend thee")); + tft.println(F("in the gobberwarts")); + tft.println(F("with my blurglecruncheon,")); + tft.println(F("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(ILI9341_BLACK); + yield(); + + 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); + yield(); + } + + 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(ILI9341_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*10, i*10)); + t += micros() - start; + tft.drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i, + tft.color565(i*10, i*10, 0)); + yield(); + } + + return t; +} + +unsigned long testRoundRects() { + unsigned long start; + int w, i, i2, + cx = tft.width() / 2 - 1, + cy = tft.height() / 2 - 1; + + tft.fillScreen(ILI9341_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)); + yield(); + } + + return micros() - start; +} diff --git a/lib/Adafruit_ILI9341/examples/mandelbrot/mandelbrot.ino b/lib/Adafruit_ILI9341/examples/mandelbrot/mandelbrot.ino new file mode 100644 index 000000000..f4164f23f --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/mandelbrot/mandelbrot.ino @@ -0,0 +1,97 @@ +#include + +#ifdef ADAFRUIT_PYPORTAL + #define TFT_D0 34 // Data bit 0 pin (MUST be on PORT byte boundary) + #define TFT_WR 26 // Write-strobe pin (CCL-inverted timer output) + #define TFT_DC 10 // Data/command pin + #define TFT_CS 11 // Chip-select pin + #define TFT_RST 24 // Reset pin + #define TFT_RD 9 // Read-strobe pin + #define TFT_BACKLIGHT 25 + // ILI9341 with 8-bit parallel interface: + Adafruit_ILI9341 tft = Adafruit_ILI9341(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST, TFT_RD); + #define USE_BUFFER // buffer all 155 KB of data for bliting - uses passive ram but looks nicer? +#else + // Use SPI + #define STMPE_CS 6 + #define TFT_CS 9 + #define TFT_DC 10 + #define SD_CS 5 + Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); +#endif + + +const int16_t + bits = 20, // Fractional resolution + pixelWidth = 320, // TFT dimensions + pixelHeight = 240, + iterations = 128; // Fractal iteration limit or 'dwell' +float + centerReal = -0.6, // Image center point in complex plane + centerImag = 0.0, + rangeReal = 3.0, // Image coverage in complex plane + rangeImag = 3.0; + +#if defined(USE_BUFFER) + uint16_t buffer[pixelWidth * pixelHeight]; +#endif + +void setup(void) { + Serial.begin(115200); + Serial.print("Mandelbrot drawer!"); + + tft.begin(); + tft.setRotation(1); + tft.fillScreen(ILI9341_BLACK); + + // Turn on backlight (required on PyPortal) +#if defined(TFT_BACKLIGHT) + pinMode(TFT_BACKLIGHT, OUTPUT); + digitalWrite(TFT_BACKLIGHT, HIGH); +#endif +} + +void loop() { + int64_t n, a, b, a2, b2, posReal, posImag; + uint32_t startTime,elapsedTime; + + + int32_t + startReal = (int64_t)((centerReal - rangeReal * 0.5) * (float)(1 << bits)), + startImag = (int64_t)((centerImag + rangeImag * 0.5) * (float)(1 << bits)), + incReal = (int64_t)((rangeReal / (float)pixelWidth) * (float)(1 << bits)), + incImag = (int64_t)((rangeImag / (float)pixelHeight) * (float)(1 << bits)); + + startTime = millis(); + posImag = startImag; + for (int y = 0; y < pixelHeight; y++) { + posReal = startReal; + for (int x = 0; x < pixelWidth; x++) { + a = posReal; + b = posImag; + for (n = iterations; n > 0 ; n--) { + a2 = (a * a) >> bits; + b2 = (b * b) >> bits; + if ((a2 + b2) >= (4 << bits)) + break; + b = posImag + ((a * b) >> (bits - 1)); + a = posReal + a2 - b2; + } + #if defined(USE_BUFFER) + buffer[y * pixelWidth + x] = (n * 29)<<8 | (n * 67); + #else + tft.drawPixel(x, y, (n * 29)<<8 | (n * 67)); // takes 500ms with individual pixel writes + #endif + posReal += incReal; + } + posImag -= incImag; + } + #if defined(USE_BUFFER) + tft.drawRGBBitmap(0, 0, buffer, pixelWidth, pixelHeight); // takes 169 ms + #endif + elapsedTime = millis()-startTime; + Serial.print("Took "); Serial.print(elapsedTime); Serial.println(" ms"); + + rangeReal *= 0.95; + rangeImag *= 0.95; +} diff --git a/lib/Adafruit_ILI9341/examples/onoffbutton/onoffbutton.ino b/lib/Adafruit_ILI9341/examples/onoffbutton/onoffbutton.ino new file mode 100644 index 000000000..3365d3385 --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/onoffbutton/onoffbutton.ino @@ -0,0 +1,125 @@ +//This example implements a simple sliding On/Off button. The example +// demonstrates drawing and touch operations. +// +//Thanks to Adafruit forums member Asteroid for the original sketch! +// +#include +#include +#include +#include +#include + +// This is calibration data for the raw touch data to the screen coordinates +#define TS_MINX 150 +#define TS_MINY 130 +#define TS_MAXX 3800 +#define TS_MAXY 4000 + +#define STMPE_CS 8 +Adafruit_STMPE610 ts = Adafruit_STMPE610(STMPE_CS); +#define TFT_CS 10 +#define TFT_DC 9 +Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); + +boolean RecordOn = false; + +#define FRAME_X 210 +#define FRAME_Y 180 +#define FRAME_W 100 +#define FRAME_H 50 + +#define REDBUTTON_X FRAME_X +#define REDBUTTON_Y FRAME_Y +#define REDBUTTON_W (FRAME_W/2) +#define REDBUTTON_H FRAME_H + +#define GREENBUTTON_X (REDBUTTON_X + REDBUTTON_W) +#define GREENBUTTON_Y FRAME_Y +#define GREENBUTTON_W (FRAME_W/2) +#define GREENBUTTON_H FRAME_H + +void drawFrame() +{ + tft.drawRect(FRAME_X, FRAME_Y, FRAME_W, FRAME_H, ILI9341_BLACK); +} + +void redBtn() +{ + tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, ILI9341_RED); + tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, ILI9341_BLUE); + drawFrame(); + tft.setCursor(GREENBUTTON_X + 6 , GREENBUTTON_Y + (GREENBUTTON_H/2)); + tft.setTextColor(ILI9341_WHITE); + tft.setTextSize(2); + tft.println("ON"); + RecordOn = false; +} + +void greenBtn() +{ + tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, ILI9341_GREEN); + tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, ILI9341_BLUE); + drawFrame(); + tft.setCursor(REDBUTTON_X + 6 , REDBUTTON_Y + (REDBUTTON_H/2)); + tft.setTextColor(ILI9341_WHITE); + tft.setTextSize(2); + tft.println("OFF"); + RecordOn = true; +} + +void setup(void) +{ + Serial.begin(9600); + tft.begin(); + if (!ts.begin()) { + Serial.println("Unable to start touchscreen."); + } + else { + Serial.println("Touchscreen started."); + } + + tft.fillScreen(ILI9341_BLUE); + // origin = left,top landscape (USB left upper) + tft.setRotation(1); + redBtn(); +} + +void loop() +{ + // See if there's any touch data for us + if (!ts.bufferEmpty()) + { + // Retrieve a point + TS_Point p = ts.getPoint(); + // Scale using the calibration #'s + // and rotate coordinate system + p.x = map(p.x, TS_MINY, TS_MAXY, 0, tft.height()); + p.y = map(p.y, TS_MINX, TS_MAXX, 0, tft.width()); + int y = tft.height() - p.x; + int x = p.y; + + if (RecordOn) + { + if((x > REDBUTTON_X) && (x < (REDBUTTON_X + REDBUTTON_W))) { + if ((y > REDBUTTON_Y) && (y <= (REDBUTTON_Y + REDBUTTON_H))) { + Serial.println("Red btn hit"); + redBtn(); + } + } + } + else //Record is off (RecordOn == false) + { + if((x > GREENBUTTON_X) && (x < (GREENBUTTON_X + GREENBUTTON_W))) { + if ((y > GREENBUTTON_Y) && (y <= (GREENBUTTON_Y + GREENBUTTON_H))) { + Serial.println("Green btn hit"); + greenBtn(); + } + } + } + + Serial.println(RecordOn); + } +} + + + diff --git a/lib/Adafruit_ILI9341/examples/onoffbutton_breakout/.esp8266.test.skip b/lib/Adafruit_ILI9341/examples/onoffbutton_breakout/.esp8266.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/Adafruit_ILI9341/examples/onoffbutton_breakout/onoffbutton_breakout.ino b/lib/Adafruit_ILI9341/examples/onoffbutton_breakout/onoffbutton_breakout.ino new file mode 100644 index 000000000..aa8cb9992 --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/onoffbutton_breakout/onoffbutton_breakout.ino @@ -0,0 +1,133 @@ +//This example implements a simple sliding On/Off button. The example +// demonstrates drawing and touch operations. +// +//Thanks to Adafruit forums member Asteroid for the original sketch! +// +#include +#include +#include +#include +#include + +//Touchscreen X+ X- Y+ Y- pins +#define YP A3 // must be an analog pin, use "An" notation! +#define XM A2 // must be an analog pin, use "An" notation! +#define YM 5 // can be a digital pin +#define XP 4 // can be a digital pin + +// This is calibration data for the raw touch data to the screen coordinates +#define TS_MINX 150 +#define TS_MINY 120 +#define TS_MAXX 920 +#define TS_MAXY 940 + +#define MINPRESSURE 10 +#define MAXPRESSURE 1000 + +// For better pressure precision, we need to know the resistance +// between X+ and X- Use any multimeter to read it +// For the one we're using, its 300 ohms across the X plate +TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); + + +#define TFT_CS 10 +#define TFT_DC 9 +Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); + +boolean RecordOn = false; + +#define FRAME_X 210 +#define FRAME_Y 180 +#define FRAME_W 100 +#define FRAME_H 50 + +#define REDBUTTON_X FRAME_X +#define REDBUTTON_Y FRAME_Y +#define REDBUTTON_W (FRAME_W/2) +#define REDBUTTON_H FRAME_H + +#define GREENBUTTON_X (REDBUTTON_X + REDBUTTON_W) +#define GREENBUTTON_Y FRAME_Y +#define GREENBUTTON_W (FRAME_W/2) +#define GREENBUTTON_H FRAME_H + +void drawFrame() +{ + tft.drawRect(FRAME_X, FRAME_Y, FRAME_W, FRAME_H, ILI9341_BLACK); +} + +void redBtn() +{ + tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, ILI9341_RED); + tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, ILI9341_BLUE); + drawFrame(); + tft.setCursor(GREENBUTTON_X + 6 , GREENBUTTON_Y + (GREENBUTTON_H/2)); + tft.setTextColor(ILI9341_WHITE); + tft.setTextSize(2); + tft.println("ON"); + RecordOn = false; +} + +void greenBtn() +{ + tft.fillRect(GREENBUTTON_X, GREENBUTTON_Y, GREENBUTTON_W, GREENBUTTON_H, ILI9341_GREEN); + tft.fillRect(REDBUTTON_X, REDBUTTON_Y, REDBUTTON_W, REDBUTTON_H, ILI9341_BLUE); + drawFrame(); + tft.setCursor(REDBUTTON_X + 6 , REDBUTTON_Y + (REDBUTTON_H/2)); + tft.setTextColor(ILI9341_WHITE); + tft.setTextSize(2); + tft.println("OFF"); + RecordOn = true; +} + +void setup(void) +{ + Serial.begin(9600); + tft.begin(); + + tft.fillScreen(ILI9341_BLUE); + // origin = left,top landscape (USB left upper) + tft.setRotation(1); + redBtn(); +} + +void loop() +{ + // Retrieve a point + TSPoint p = ts.getPoint(); + + // See if there's any touch data for us + if (p.z > MINPRESSURE && p.z < MAXPRESSURE) + { + // Scale using the calibration #'s + // and rotate coordinate system + p.x = map(p.x, TS_MINY, TS_MAXY, 0, tft.height()); + p.y = map(p.y, TS_MINX, TS_MAXX, 0, tft.width()); + int y = tft.height() - p.x; + int x = p.y; + + if (RecordOn) + { + if((x > REDBUTTON_X) && (x < (REDBUTTON_X + REDBUTTON_W))) { + if ((y > REDBUTTON_Y) && (y <= (REDBUTTON_Y + REDBUTTON_H))) { + Serial.println("Red btn hit"); + redBtn(); + } + } + } + else //Record is off (RecordOn == false) + { + if((x > GREENBUTTON_X) && (x < (GREENBUTTON_X + GREENBUTTON_W))) { + if ((y > GREENBUTTON_Y) && (y <= (GREENBUTTON_Y + GREENBUTTON_H))) { + Serial.println("Green btn hit"); + greenBtn(); + } + } + } + + Serial.println(RecordOn); + } +} + + + diff --git a/lib/Adafruit_ILI9341/examples/pictureEmbed/.leonardo.test.skip b/lib/Adafruit_ILI9341/examples/pictureEmbed/.leonardo.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/Adafruit_ILI9341/examples/pictureEmbed/dragon.h b/lib/Adafruit_ILI9341/examples/pictureEmbed/dragon.h new file mode 100644 index 000000000..2bd9050bb --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/pictureEmbed/dragon.h @@ -0,0 +1,860 @@ +#define DRAGON_WIDTH 110 +#define DRAGON_HEIGHT 70 + +const PROGMEM uint16_t dragonBitmap[DRAGON_WIDTH * DRAGON_HEIGHT] = { + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0X8C10, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XBD14, 0XAC92, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XDEFB, 0XB431, 0XA1C9, 0XAA4B, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XCE18, 0X9A2A, 0X9044, + 0XDEFB, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XDEFB, 0XB3B0, 0XA148, 0XB865, 0XC066, 0XC066, 0XAA4B, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0XAB2E, 0X9864, + 0XA865, 0XAAAC, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0XAC92, + 0X728B, 0XDEFB, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XCE18, 0XAB2E, 0XAA4B, 0XAA4B, 0XAA4B, 0XA3AF, + 0XB431, 0XB431, 0XB431, 0XA411, 0XA8E6, 0XD086, 0XB865, 0X7043, 0XA865, + 0XD086, 0XAA4B, 0XCE79, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XB493, 0XA8E6, + 0X9864, 0XC066, 0XD086, 0XB493, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XCE79, 0X830D, + 0X80C5, 0XAB2E, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XB431, 0XC066, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XB865, 0X9864, + 0XC066, 0XD086, 0XB8E7, 0XB493, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XDE9A, 0XA1C9, + 0XB865, 0X9864, 0XD086, 0XD086, 0XD086, 0XBDF7, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0XAD14, 0X71A8, + 0X9864, 0XC066, 0XC515, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XDEFB, 0XBDF7, 0XBD14, 0XA411, 0XB431, 0XB148, 0XD086, 0XC066, + 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, 0XD086, 0XA865, + 0XB865, 0XD086, 0XD086, 0XB1CA, 0XDEFB, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0XB3B0, + 0XB865, 0XA865, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, 0XBD14, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XCE79, 0X932D, 0X78C5, + 0XA865, 0XD086, 0XC066, 0XC515, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XDEFB, 0XAB2E, 0XA0C6, 0XB865, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, + 0XB865, 0X78C5, 0X4208, 0X2104, 0X8947, 0X9864, 0X9864, 0X9864, 0XD086, + 0XD086, 0XA865, 0X9864, 0XD086, 0XB865, 0XDEFB, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XCE18, + 0XB148, 0XC066, 0XA865, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XAA4B, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0XB493, 0X89A8, 0X9044, + 0XB865, 0XD086, 0XD086, 0XC066, 0XC515, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0X7B8E, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XAD14, 0XDEFB, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XB431, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, 0XA865, + 0X9864, 0XD086, 0XD086, 0XD086, 0XAA4B, 0X9A2A, 0XC066, 0XD086, 0XD086, + 0XD086, 0XD086, 0XC066, 0X9044, 0X9864, 0X9864, 0X9044, 0XCE18, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, + 0XB3B0, 0XC066, 0XB865, 0XA865, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XAAAC, 0XBD96, 0XAD14, 0XAAAC, 0X8044, 0X9864, + 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, 0XB431, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XB3B0, 0XAA4B, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XBD96, 0XB8E7, 0XB431, 0XEF7D, 0XFFFF, + 0XFFFF, 0XFFFF, 0XA32E, 0X9864, 0X9864, 0XAA4B, 0XAA4B, 0X9147, 0XB865, + 0X9044, 0XD086, 0XA865, 0XD086, 0XC066, 0XC066, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, + 0XAA4B, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XDE9A, 0XB148, 0XD086, 0XA865, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XA865, 0X9864, 0X9864, 0XA865, 0X9864, + 0XC066, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XA148, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XC515, 0XC066, 0XAA4B, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XAA4B, 0XD086, + 0XA865, 0XAB2E, 0XBD96, 0XEF7D, 0XCE79, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XDEFB, 0XB1CA, 0XD086, 0XD086, 0X9864, 0X822A, 0XBDF7, 0XB493, + 0XB148, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XA865, 0X9864, + 0XD086, 0XD086, 0XD086, 0XAA4B, 0XBD14, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XB431, 0XB865, 0XD086, 0X9864, 0XC066, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XC066, 0X9864, 0X9864, 0XC066, 0XA865, 0X9864, + 0XC066, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XB3B0, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XCD96, 0XC066, 0XD086, 0XAA4B, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XDE9A, 0XB865, 0XD086, 0XD086, 0XD086, 0XA865, 0X9147, 0X9C92, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XCE18, 0X80C5, 0X5822, 0X5A08, 0XDEFB, + 0XFFFF, 0XFFFF, 0XCE79, 0XA0C6, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0X9044, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XAB2E, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XEF7D, 0XAA4B, 0XD086, 0XC066, 0X9864, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XA865, 0X9864, 0XA865, 0XD086, 0X9864, 0X9864, + 0XC066, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XAB2E, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XDE9A, 0XB8E7, 0XD086, 0XD086, + 0XAA4B, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XC515, 0XC066, 0XD086, 0XB865, 0X8044, 0X82AB, + 0XBDF7, 0XBDF7, 0XBDF7, 0XBDF7, 0XBDF7, 0XBDF7, 0XAC92, 0XA3AF, 0X92AC, + 0XAB2E, 0XAA4B, 0XA1C9, 0X9864, 0XB865, 0XA865, 0XC066, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, 0XB1CA, 0XBD14, + 0X9CF3, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XCE18, 0XA8E6, 0XD086, 0XC066, 0XA865, 0XD086, 0XD086, + 0XD086, 0XD086, 0XC066, 0X9864, 0X9864, 0XC066, 0XD086, 0X9864, 0X9864, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XC066, 0XC515, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XDE9A, 0XB8E7, 0XD086, + 0XD086, 0XD086, 0XAA4B, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XB3B0, 0XD086, 0XA865, + 0XC066, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XC066, 0XA865, 0X8044, 0X4822, 0X9044, 0X9044, + 0XD086, 0XD086, 0XD086, 0XD086, 0X8044, 0X9864, 0XD086, 0XD086, 0XD086, + 0XAA4B, 0XCE18, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XB493, 0XC066, 0XD086, 0XC066, 0XA865, 0XD086, + 0XD086, 0XD086, 0XA865, 0X9864, 0XA865, 0XD086, 0XC066, 0X9864, 0X9864, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0X9044, 0X3986, 0XCE79, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XDE9A, 0XB8E7, + 0XD086, 0XD086, 0XC066, 0XD086, 0XAA4B, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XB431, 0XC066, 0X90C6, 0XAA4B, 0XAA4B, 0XAA4B, 0XAA4B, 0X99C9, 0X9864, + 0X9864, 0X9864, 0X6843, 0X6843, 0X5822, 0X4822, 0X7043, 0X9044, 0XC066, + 0X9864, 0X7043, 0XC066, 0XB865, 0XD086, 0X7043, 0X8044, 0XC066, 0XD086, + 0XD086, 0XD086, 0XD086, 0XC066, 0XB431, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0XAB2E, 0XD086, 0XD086, 0XC066, 0XA865, + 0XD086, 0XD086, 0X9864, 0X9044, 0XC066, 0XD086, 0XC066, 0X9864, 0XA865, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XB865, 0X9864, 0X9864, 0X9864, 0X9044, 0X6843, 0X6843, 0X8044, 0X7B0C, + 0XCE79, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XDE9A, + 0XB8E7, 0XD086, 0X9864, 0XC066, 0X9864, 0XD086, 0XAA4B, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XC515, 0XA8E6, 0XCE18, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XA148, 0XD086, 0XD086, 0XD086, 0XD086, + 0XB148, 0XBD14, 0X9A2A, 0XD086, 0X9864, 0X9864, 0X6843, 0XD086, 0XC066, + 0XC066, 0XD086, 0XD086, 0XD086, 0XA865, 0XC066, 0XA865, 0XEF7D, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0XB1CA, 0XD086, 0XD086, 0XD086, + 0X9864, 0XC066, 0X9864, 0X9864, 0XD086, 0XD086, 0XC066, 0X9864, 0XA865, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, 0X9864, 0X9864, 0X9864, + 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, 0XA865, 0XD086, 0XA1C9, + 0XDE9A, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XDE9A, 0XB8E7, 0XD086, 0XD086, 0X9864, 0XD086, 0X9864, 0XD086, 0XAA4B, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XDEFB, 0XCE18, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XAA4B, 0XD086, 0XC066, + 0XD086, 0XAAAC, 0XEF7D, 0XFFFF, 0X932D, 0XB865, 0XB865, 0XC066, 0X7043, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XB148, 0XEF7D, 0XBD14, 0XA148, + 0XBDF7, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XAA4B, 0XD086, 0XD086, + 0XD086, 0X8044, 0X9864, 0XA865, 0XD086, 0XD086, 0XC066, 0X9864, 0XA865, + 0XD086, 0XD086, 0XA865, 0X9864, 0X9864, 0X9864, 0X9864, 0XA865, 0XD086, + 0XB865, 0X9864, 0X9864, 0X9864, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, + 0XAB2E, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XDE9A, 0XB8E7, 0XD086, 0XD086, 0XC066, 0XA865, 0XD086, 0X9864, + 0XD086, 0XA0C6, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0X8C10, 0XB431, 0XEF7D, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XBD14, + 0XD086, 0XA1C9, 0X80C5, 0XEF7D, 0XFFFF, 0XB431, 0XD086, 0XB865, 0X9864, + 0X8044, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XAA4B, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XBD14, 0XD086, + 0XD086, 0XD086, 0XA865, 0XB865, 0XD086, 0XD086, 0XC066, 0X9864, 0X9864, + 0X9864, 0X9044, 0X9864, 0X9864, 0XC066, 0XD086, 0XD086, 0XB865, 0X9864, + 0X9864, 0X9864, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XAB2E, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XDE9A, 0XB8E7, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, + 0XD086, 0X9864, 0XD086, 0X9864, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0XB148, + 0XA1C9, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XBDF7, 0XD086, 0XBD14, 0XEF7D, 0XFFFF, 0XFFFF, 0XA148, 0XD086, + 0XD086, 0XD086, 0X9864, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XC066, 0XB431, 0XEF7D, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XAA4B, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0XC066, 0X7043, + 0X9864, 0XA865, 0XD086, 0XD086, 0XD086, 0XC066, 0X9864, 0X9864, 0X9864, + 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XAAAC, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XA32E, 0XAA4B, 0XAA4B, 0XAA4B, 0XAB2E, + 0X9044, 0XD086, 0XA865, 0X9864, 0XD086, 0XD086, 0XBDF7, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0XBDF7, 0XDEFB, 0XFFFF, 0XFFFF, + 0XFFFF, 0XB431, 0XB865, 0XAC92, 0XFFFF, 0XEF7D, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0X8947, 0XEF7D, 0XFFFF, 0XFFFF, 0XEF7D, + 0X9044, 0XD086, 0XD086, 0XB865, 0XB865, 0X9864, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XA0C6, 0XEF7D, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0X9864, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0X9864, 0X9864, 0XA865, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XA8E6, 0XEF7D, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XCE18, 0XC066, 0XD086, 0XA1C9, 0XB431, 0XD086, 0XD086, 0XB493, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0XAB2E, 0XB865, + 0XB3B0, 0XFFFF, 0XFFFF, 0X9AAC, 0XB865, 0XA148, 0XFFFF, 0X9AAC, 0XCD96, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XAA4B, 0XB865, 0X9864, 0X9864, 0X8044, 0X9044, 0X8044, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XA1C9, 0X90C6, 0XD086, 0XB3B0, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0X9864, 0XD086, 0XD086, 0X9864, 0XD086, + 0XD086, 0XD086, 0XD086, 0X9864, 0X9864, 0X9864, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XAB2E, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XDEFB, 0XB3B0, 0XAA4B, 0X9864, 0X9864, 0XA1C9, + 0XAB2E, 0XBD96, 0XFFFF, 0XAA4B, 0XD086, 0XD086, 0XB493, 0XFFFF, 0XAB2E, + 0XC066, 0XAAAC, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0X8947, 0X9864, 0X9A2A, 0XFFFF, 0X9864, 0XD086, 0XA148, 0XEF7D, + 0XA8E6, 0XB431, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XCE79, 0XC066, 0XD086, 0XD086, 0XD086, 0X9044, 0X7043, + 0X8044, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0XFFFF, 0XFFFF, 0XBD14, + 0X92AC, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0X9864, 0XD086, 0XB865, + 0XB865, 0XD086, 0XD086, 0X8044, 0X6843, 0X9864, 0X9864, 0X9864, 0X9864, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XBD96, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XB431, 0XB865, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0X9147, 0XA865, 0XD086, 0XC066, 0XCE79, + 0XFFFF, 0XFFFF, 0XCD96, 0X7926, 0XEF7D, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0X9864, 0XD086, 0XC066, 0X9CF3, 0XC066, 0XD086, + 0X8A2A, 0X8947, 0XD086, 0XBD14, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XAD14, 0XC066, 0XD086, 0XD086, 0XD086, + 0X9864, 0X9864, 0X8044, 0XD086, 0XD086, 0XD086, 0XD086, 0XB865, 0XDEFB, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0X9864, + 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, + 0XC066, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XBDF7, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XB493, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, 0XA865, + 0XA148, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XAA4B, 0XD086, 0XD086, 0X71A8, + 0XD086, 0XD086, 0X9044, 0XD086, 0X9864, 0XBDF7, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XDE9A, 0XB865, 0XA865, 0XA865, + 0XD086, 0XD086, 0X7043, 0X9864, 0X9044, 0XA865, 0XD086, 0XD086, 0XD086, + 0XD086, 0XA8E6, 0XB493, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0X9864, 0XB865, 0XB865, 0XD086, 0XD086, 0XD086, 0X9864, 0X9864, + 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XA865, 0X9864, 0X9864, 0X9864, 0X9864, 0XD086, 0XD086, + 0XD086, 0XD086, 0XBDF7, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XA0C6, 0XD086, + 0XD086, 0XD086, 0XA865, 0X80C5, 0X9864, 0XC066, 0XD086, 0XD086, 0XD086, + 0XD086, 0XC066, 0X8B8E, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XB493, 0XD086, + 0XD086, 0X9864, 0XA865, 0XA865, 0XC066, 0XD086, 0XA8E6, 0XEF7D, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XAAAC, 0XD086, + 0XD086, 0XC066, 0X9864, 0X8044, 0XA865, 0X9044, 0XC066, 0X8044, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9A2A, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XEF7D, 0XA865, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, + 0X9864, 0X9864, 0XC066, 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, 0X9864, + 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, 0XD086, 0XD086, + 0X9864, 0X9864, 0X9864, 0XC066, 0XAB2E, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0X9864, 0XD086, 0XD086, 0X90C6, 0XCE79, 0XFFFF, 0XFFFF, 0X9C92, 0X9864, + 0XD086, 0XD086, 0XD086, 0XD086, 0XAB2E, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XEF7D, 0XA0C6, 0XD086, 0XD086, 0XB865, 0X9864, 0XD086, 0XB1CA, 0XDE9A, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XAA4B, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0X7043, 0XA865, 0X9044, + 0X9044, 0XD086, 0XD086, 0XD086, 0XD086, 0XB1CA, 0XB431, 0X82AB, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XBD14, 0XA865, 0XC066, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XA865, 0X9864, 0XC066, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, 0X7043, 0X8044, 0XDEFB, 0XFFFF, + 0XFFFF, 0XFFFF, 0X9864, 0XD086, 0XD086, 0XA32E, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0X728B, 0XB865, 0XD086, 0XD086, 0XD086, 0X9864, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0XAA4B, 0XD086, 0XD086, 0XB865, 0X90C6, + 0XEF7D, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XC515, 0X9044, 0XC066, 0XD086, 0XD086, 0XD086, 0X9864, 0X8044, + 0XC066, 0X8044, 0X9044, 0XC066, 0XD086, 0XD086, 0XD086, 0XBDF7, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XEF7D, 0X6926, 0XC066, 0XD086, + 0XD086, 0XD086, 0X9044, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, 0XA865, + 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XA8E6, 0XAAAC, 0XB431, 0X8410, + 0X8C71, 0XFFFF, 0XFFFF, 0XFFFF, 0XAAAC, 0XD086, 0XD086, 0XA865, 0XA32E, + 0XB431, 0XB431, 0X8947, 0XC066, 0X9864, 0XD086, 0XD086, 0XD086, 0XC066, + 0XCE79, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XB431, 0XD086, 0XD086, + 0XD086, 0XB8E7, 0XDEFB, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XA0C6, 0XD086, 0XA865, 0X9864, 0X9864, 0X9864, + 0X9044, 0X9044, 0XA865, 0X9864, 0XA865, 0X9864, 0XD086, 0XD086, 0XD086, + 0XB3B0, 0XFFFF, 0XEF7D, 0XBDF7, 0XB431, 0XAAAC, 0XA148, 0XA865, 0XD086, + 0XD086, 0XD086, 0XC066, 0XD086, 0XB865, 0X9044, 0X9864, 0X9864, 0XC066, + 0XD086, 0XD086, 0XD086, 0X9864, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XB8E7, 0XB493, 0XEF7D, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XDE9A, 0XB8E7, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, + 0XD086, 0XD086, 0XBDF7, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XAAAC, + 0XD086, 0XD086, 0XD086, 0XD086, 0XB3B0, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XDEFB, 0XB865, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0X8044, 0X9864, 0XB865, 0X8044, 0XC066, 0X6843, 0XD086, + 0XD086, 0X9864, 0XA0C6, 0X99C9, 0XA865, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0X8044, 0XB865, 0XD086, 0XC066, 0XA865, + 0XD086, 0XA865, 0X9864, 0X9864, 0XD086, 0XD086, 0XB865, 0X9864, 0XC066, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XB1CA, 0XDEFB, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XDEFB, 0XAB2E, 0X9864, 0XC066, 0XD086, 0XC066, 0X9864, 0X89A8, 0XA865, + 0XD086, 0XD086, 0XD086, 0XA865, 0XEF7D, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XAA4B, 0XD086, 0XD086, 0XD086, 0XD086, 0XA148, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XBDF7, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0X7043, 0XC066, 0X6843, 0XD086, 0X7043, + 0XC066, 0X9044, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XA865, 0XD086, 0X9864, 0XB865, 0XB865, + 0XD086, 0XC066, 0XA865, 0XD086, 0XD086, 0XD086, 0X9864, 0X9864, 0XC066, + 0XD086, 0XA865, 0XA865, 0XD086, 0XD086, 0XD086, 0XD086, 0XA865, 0X9C92, + 0XBDF7, 0XBDF7, 0XBDF7, 0XBDF7, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XCE79, 0XBDF7, 0XCE79, 0XEF7D, + 0XAAAC, 0XD086, 0XD086, 0XD086, 0XD086, 0XA1C9, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XA148, 0XD086, 0XD086, 0XD086, 0XD086, 0XB865, + 0XDEFB, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XCD96, + 0X8044, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X6843, 0X9044, 0X9864, + 0XA865, 0XA865, 0X9044, 0XB865, 0XD086, 0XD086, 0XB865, 0X9864, 0X8044, + 0XC066, 0XD086, 0XD086, 0XB865, 0XD086, 0XA865, 0X9044, 0XD086, 0XB865, + 0XB865, 0XB865, 0XA865, 0XD086, 0XB865, 0XB865, 0XD086, 0XD086, 0XD086, + 0XD086, 0XA865, 0X9864, 0XB865, 0XC066, 0X9864, 0XD086, 0XD086, 0XD086, + 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0X9864, 0XA1C9, + 0XAB2E, 0XBD14, 0XCE79, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XCE79, + 0XB493, 0XB148, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XBD14, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0X9864, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XBDF7, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XA148, 0XD086, 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, 0X9864, + 0X9864, 0X9864, 0X7043, 0X9864, 0XD086, 0XD086, 0XA865, 0X9864, 0XB865, + 0XD086, 0X9864, 0XD086, 0XC066, 0X7043, 0XD086, 0XD086, 0X9864, 0XC066, + 0XA865, 0XD086, 0X9864, 0XD086, 0XC066, 0XA865, 0XD086, 0XA865, 0XC066, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, 0X9864, 0X9044, 0X9864, + 0XC066, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, 0X9864, 0X9864, 0X9864, 0X9864, + 0X9864, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XA1C9, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XC515, 0XC066, 0XA865, + 0XD086, 0XD086, 0XD086, 0XD086, 0XAD14, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0X9044, 0X8044, 0X9864, 0XA865, 0XD086, 0XA865, 0X9864, 0XC066, + 0XD086, 0XD086, 0XA865, 0XC066, 0XC066, 0XA865, 0X9864, 0XD086, 0XA865, + 0XC066, 0XD086, 0X9864, 0XD086, 0X9864, 0XD086, 0XD086, 0XB865, 0XB865, + 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0X9864, 0X8044, 0X8044, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, + 0XA865, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XB8E7, 0XDEFB, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, + 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0XFFFF, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X04A6, 0XB865, + 0XD086, 0X31C3, 0XC066, 0XD086, 0XD086, 0X9864, 0XB865, 0X2305, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X9864, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0X6843, 0X9864, 0XA865, 0XB865, 0X9864, 0XC066, + 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0XC066, 0XA865, 0XD086, 0X9864, + 0XD086, 0X9864, 0XD086, 0XD086, 0XA865, 0XC066, 0X9864, 0XD086, 0XD086, + 0XD086, 0XC066, 0XA865, 0XB865, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, 0X8044, 0X8044, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0X9864, 0XC066, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0X8145, 0X0C66, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0506, + 0X8145, 0XD086, 0X9105, 0X0506, 0X80E5, 0XD086, 0XD086, 0X9864, 0XD086, + 0X9864, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X80E5, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X8044, 0XB865, 0X9864, 0XB865, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XA865, 0XC066, 0X9864, 0XD086, + 0XD086, 0X9864, 0XC066, 0XA865, 0XD086, 0XD086, 0XD086, 0X9864, 0X9864, + 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, 0XA865, 0X9864, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XA865, 0X9864, 0X9864, 0X7043, 0X9864, 0XA865, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XA865, 0XC066, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XC066, 0X59E5, 0X0506, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X2305, 0XB865, 0XD086, 0X2305, 0X0547, 0X1B65, 0XD086, 0XD086, + 0XB865, 0XB865, 0X9864, 0X6985, 0X1B65, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0C05, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0X9044, 0X8044, 0X9864, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0X9864, + 0XD086, 0XD086, 0XD086, 0X9864, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, + 0X9864, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XB865, 0XA865, + 0XA865, 0XD086, 0XD086, 0XA865, 0X9864, 0XC066, 0XD086, 0XD086, 0X9864, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XA865, 0XC066, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0X80E5, 0X0C05, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X1B65, 0XC066, 0X8044, 0X04A6, 0X0547, 0X0547, + 0X80E5, 0XD086, 0XD086, 0X9864, 0XA865, 0XD086, 0XD086, 0X1AC4, 0X0405, + 0X0547, 0X0547, 0X4285, 0XC066, 0X9864, 0X9864, 0X9864, 0X9864, 0X8044, + 0XA865, 0XD086, 0XD086, 0XC066, 0XA865, 0XD086, 0XD086, 0XD086, 0XD086, + 0X7043, 0XC066, 0XD086, 0XD086, 0XD086, 0X9864, 0X9864, 0XD086, 0XD086, + 0XD086, 0XD086, 0XA865, 0XA865, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XB865, 0X8044, 0XD086, 0XA865, 0XC066, 0XD086, 0XD086, 0XD086, + 0XD086, 0XC066, 0XA865, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XA865, 0X59E5, 0X0BA5, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0506, 0X3264, 0X98A5, 0X7145, 0X04A6, 0X0547, + 0X0547, 0X0547, 0X1BC5, 0XC066, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, + 0X9864, 0XD086, 0X7145, 0X4984, 0X80A4, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0X9864, 0X7043, 0X9864, 0X9864, 0XB865, 0X9864, 0XD086, 0XD086, + 0XD086, 0X8044, 0XA865, 0XD086, 0XD086, 0XD086, 0XD086, 0X9044, 0XA865, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0X9864, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0X8044, 0XC066, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XA865, 0XC066, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9044, 0XA865, 0X9864, + 0X6985, 0X4285, 0X1B65, 0X0506, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0445, 0X0445, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X4A25, 0XD086, 0XD086, 0XD086, + 0X9864, 0X9864, 0XC066, 0XD086, 0XD086, 0X9864, 0X9864, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, 0X9864, + 0XD086, 0XB865, 0X9864, 0X4822, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0X6843, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0X9864, + 0XD086, 0XC066, 0X9864, 0X8044, 0X9864, 0X9864, 0X9864, 0X8044, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XA865, 0X9864, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XA8C5, 0X32C5, 0X0506, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X4A25, + 0XD086, 0XD086, 0XD086, 0XA865, 0X9864, 0XB865, 0XD086, 0X9864, 0X9864, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, + 0XD086, 0X9864, 0XA865, 0XB865, 0XD086, 0X8044, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0X6843, 0XD086, 0X9044, 0X9864, 0X9044, 0X9864, 0XB865, + 0X9864, 0X9864, 0XB865, 0XA865, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, + 0XA865, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XC066, 0X9864, 0XA865, 0X9864, 0X9044, 0XA865, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0X98A5, 0X32C5, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X32C5, 0XC066, 0XD086, 0XD086, 0XD086, 0XB865, 0X9864, + 0X8044, 0X9864, 0X8044, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, + 0XD086, 0XD086, 0XD086, 0X9864, 0XC066, 0XA865, 0X9864, 0X9864, 0XA865, + 0X9864, 0X9044, 0X9864, 0XD086, 0X6843, 0X9864, 0XC066, 0XA865, 0XD086, + 0XD086, 0XB865, 0X8044, 0X7043, 0XB865, 0XD086, 0X9864, 0X9864, 0X9864, + 0X9864, 0X9864, 0XA865, 0XB865, 0X9864, 0X9864, 0X9864, 0XA865, 0XD086, + 0XD086, 0XD086, 0X9044, 0XD086, 0XD086, 0XC066, 0XA865, 0XC066, 0X9864, + 0X9864, 0X9044, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X80E5, + 0X1BC5, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X4285, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, + 0XB865, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, 0X9044, 0X7043, 0XD086, + 0X8044, 0X9044, 0XD086, 0XD086, 0X9044, 0X9864, 0X6843, 0XD086, 0XD086, + 0X9864, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0XB865, 0XA865, + 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X6985, + 0X04A6, 0X4A25, 0X80A4, 0XA865, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, + 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0X9864, 0X8044, 0X9864, 0XC066, + 0XD086, 0XD086, 0XC066, 0X6985, 0X0C66, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X4A25, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, + 0XD086, 0XD086, 0XA865, 0X9044, 0XB865, 0XA865, 0X9864, 0X9044, 0X9864, + 0XB865, 0X9044, 0XC066, 0X9864, 0XD086, 0XD086, 0X9864, 0XD086, 0XA865, + 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, 0X9864, 0XC066, 0X9864, + 0XB865, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0X4A25, 0X0506, 0X0547, 0X0547, 0X1BC5, 0XA8C5, 0XD086, 0XD086, 0XB865, + 0XB865, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, 0X9864, + 0XD086, 0X9864, 0XB865, 0XD086, 0XD086, 0XD086, 0XB865, 0X32C5, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0506, 0X8145, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, + 0XA865, 0XD086, 0XD086, 0XB865, 0XB865, 0XD086, 0XB865, 0X8044, 0XB865, + 0XA865, 0X8044, 0XA865, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0X9864, + 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0X8044, 0X7043, + 0X7043, 0XC066, 0XD086, 0XC066, 0XA865, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0X6985, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X04A6, 0X2305, + 0X32C5, 0X6944, 0XD086, 0XD086, 0XD086, 0X9864, 0XC066, 0XD086, 0XD086, + 0X9864, 0XD086, 0XD086, 0X9864, 0XB865, 0XB865, 0XD086, 0XC066, 0XB865, + 0XD086, 0X7145, 0X0C66, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0C66, 0XB865, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XA865, 0XC066, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, + 0X9044, 0XB865, 0X9044, 0XD086, 0XD086, 0XD086, 0XC066, 0XA865, 0XD086, + 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, 0XB865, 0XA865, 0XD086, 0XB865, + 0XB865, 0XD086, 0XD086, 0XB865, 0X9864, 0X7043, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0X32C5, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0405, 0X2305, 0X0BA5, 0X0BA5, 0X7145, + 0X9864, 0X90A4, 0X58E3, 0XD086, 0XB865, 0XB865, 0XD086, 0X9864, 0XD086, + 0XD086, 0X1A63, 0XA8C5, 0XD086, 0XA8C5, 0X0C66, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X32C5, 0XD086, + 0XD086, 0XD086, 0XD086, 0XA865, 0X90A4, 0X4183, 0X6985, 0X5924, 0X9044, + 0XD086, 0XB865, 0XB865, 0X9044, 0X9864, 0XD086, 0XD086, 0XD086, 0X9864, + 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0XD086, 0XA8C5, 0X1B65, 0X0405, + 0X0405, 0X9044, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X9864, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X4A25, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0405, 0X1AC4, 0XD086, 0XD086, + 0X9864, 0XD086, 0XD086, 0X4285, 0X04A6, 0X59E5, 0X9864, 0X4224, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0506, 0X98A5, 0XD086, 0XD086, 0X22C4, 0X0506, 0X0547, 0X0547, 0X0547, + 0X2305, 0XD086, 0XA865, 0X8044, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0X9864, 0XD086, 0XD086, 0X9044, 0XD086, 0XB865, 0X6985, 0X04A6, + 0X0547, 0X0547, 0X0547, 0X90A4, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XA865, 0XC066, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XB865, 0X0BA5, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X22C4, 0XA865, 0X9044, 0X9864, 0XD086, 0X6985, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X4A25, 0XD086, 0X98A5, 0X0506, 0X0547, 0X0547, + 0X0547, 0X0506, 0X4183, 0X9864, 0X9044, 0XB865, 0XD086, 0XD086, 0XD086, + 0X9864, 0XD086, 0XD086, 0X9864, 0XD086, 0X7145, 0X0BA5, 0X0405, 0X04A6, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X32C5, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0X9044, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0X90A4, 0X4285, 0X04A6, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0405, 0XD086, 0XD086, 0X9864, 0XD086, 0X6985, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X1B65, 0XD086, 0X4A25, 0X0547, + 0X0547, 0X0547, 0X0547, 0X4A25, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, + 0XD086, 0X8145, 0X0B64, 0XD086, 0XD086, 0XA865, 0X32C5, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X2305, + 0X98A5, 0XC066, 0X9864, 0X9864, 0X9864, 0XC066, 0X9864, 0XA865, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XA865, 0X80E5, 0X59E5, + 0X0C05, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0445, 0XC066, 0XD086, 0X9044, 0XD086, + 0X6985, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0405, 0X98A5, + 0X0506, 0X0547, 0X0547, 0X0547, 0X0547, 0X59E5, 0XD086, 0XB865, 0XB865, + 0XD086, 0XD086, 0XA8C5, 0X0C66, 0X0506, 0X98A5, 0XD086, 0X90A4, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0506, 0X70E4, 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, + 0X9864, 0X9864, 0X9864, 0X9044, 0X9864, 0XC066, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XC066, 0X1BC5, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X68E4, 0X9044, + 0X9864, 0XD086, 0X6985, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0445, 0X1BC5, 0X0547, 0X0547, 0X0547, 0X0547, 0X0C66, 0X90A4, 0XA865, + 0X9044, 0XD086, 0XD086, 0XA8C5, 0X0C66, 0X0547, 0X0547, 0X32C5, 0XD086, + 0X4A25, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0C66, 0X7145, 0X9864, 0X9864, + 0XA865, 0XA865, 0XD086, 0XD086, 0X9864, 0XD086, 0XD086, 0X9864, 0XD086, + 0XD086, 0X90A4, 0X80E5, 0XC066, 0XD086, 0X7145, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X9864, 0XD086, 0X9864, 0XD086, 0X6985, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X80E5, + 0XD086, 0XB865, 0XB865, 0XD086, 0XA8C5, 0X0C66, 0X0547, 0X0547, 0X0547, + 0X0506, 0X9044, 0X04A6, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X4285, 0XB865, 0XA865, 0X9864, 0XC066, 0XD086, 0XA865, + 0XC066, 0XD086, 0XC066, 0X0C05, 0X0547, 0X0445, 0X4285, 0X58E3, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X9864, 0XD086, 0X9864, 0XD086, 0X4285, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X9864, 0XD086, 0X9864, 0XD086, 0XA8C5, 0X0C66, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0405, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0405, 0X04A6, 0X2305, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0C66, 0X80E5, 0X9864, 0X9864, 0X9864, + 0X8044, 0XB865, 0XD086, 0XD086, 0X32C5, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X4285, 0XA865, 0X7043, 0XB865, 0XD086, 0X2305, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X2365, 0X9864, 0XB865, 0XB865, 0XA8C5, 0X0C66, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0506, 0X70A4, 0XA865, 0X8044, 0XD086, 0X7145, 0X0506, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X04A6, 0X3224, 0XD086, 0XD086, + 0XC066, 0X9864, 0XC066, 0XD086, 0XD086, 0X4A25, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X1B65, 0X32C5, 0X68E4, 0X2365, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X9864, 0XD086, 0X9864, 0XD086, + 0XC066, 0X0445, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X2305, 0X6985, 0X59E5, 0X22C4, 0X0445, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0C66, 0XA8C5, 0XA865, 0XA865, 0XD086, 0X2305, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0BA5, 0X0C05, 0X0C66, 0X9044, 0XD086, 0XD086, 0X8145, + 0X04A6, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0C05, 0XB865, 0XC066, + 0X9864, 0X9044, 0XA865, 0XD086, 0XD086, 0XD086, 0X4A25, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X22C4, 0X70E4, 0XB865, 0X9864, 0XC066, + 0X2365, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X5924, 0X9864, + 0XA865, 0XD086, 0X9864, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X3224, 0X22C4, 0X9864, 0XA865, 0XC066, 0X6985, + 0X0BA5, 0X0506, 0X0547, 0X0506, 0X32C5, 0XB865, 0XD086, 0XD086, 0XD086, + 0X80E5, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0C05, 0X4A25, + 0X80E5, 0XD086, 0XB865, 0X4A25, 0X0BA5, 0X22C4, 0X6985, 0X90A4, 0X9864, + 0X9864, 0X9864, 0X9044, 0XD086, 0XD086, 0XD086, 0XB865, 0X2305, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X04A6, 0X0547, 0X1B04, + 0XC066, 0XD086, 0XC066, 0X4285, 0X0506, 0X0547, 0X0547, 0X0547, 0X4A25, + 0XD086, 0XD086, 0XD086, 0XD086, 0X59E5, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X7043, 0XA865, + 0XD086, 0XD086, 0XB865, 0X7043, 0X6843, 0X9864, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0X1B65, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0506, 0X32C5, 0X3264, 0X6985, + 0X59E5, 0X04A6, 0X0547, 0X4A25, 0XC066, 0X9864, 0XA865, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, 0X6985, 0X04A6, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0C66, 0X59E5, 0X6883, 0X9864, 0X9864, 0XD086, 0X98A5, 0X4285, 0X32C5, + 0X6944, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X2305, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0506, 0X32C5, 0X6985, 0X6944, + 0XC066, 0XC066, 0X9864, 0X9864, 0XB865, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XA865, 0X0506, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0506, 0X80E5, 0X9864, + 0X9864, 0XD086, 0XD086, 0XB865, 0X6985, 0X6985, 0X9044, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XA865, 0X59E5, 0X0C05, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0506, 0X6985, 0XA865, 0XA865, 0XD086, 0XD086, 0XD086, 0X9864, 0XB865, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X6985, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X3264, 0X7145, + 0X6985, 0X9864, 0X9044, 0X9864, 0X9864, 0X9864, 0XA865, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X90A4, 0X6985, 0X4285, + 0X1BC5, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0445, + 0X2305, 0X04A6, 0X90A4, 0XC066, 0X9864, 0X9044, 0X9864, 0X9864, 0X9864, + 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0X58E3, 0X3264, + 0X1B65, 0X0506, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X4224, 0X4224, 0X3264, 0X7043, 0X4A25, 0X32C5, 0X49C4, + 0X9864, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0X7145, 0X1B65, 0X0506, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X04A6, 0X0547, 0X0547, 0X04A6, 0X80E5, 0XD086, 0XD086, 0XD086, 0XD086, + 0XD086, 0XD086, 0XD086, 0XD086, 0XC066, 0X7145, 0X49C4, 0XB865, 0XD086, + 0XD086, 0XD086, 0X9044, 0X9864, 0X32C5, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0445, 0X5883, 0XA865, 0XD086, 0XD086, + 0XC066, 0X9864, 0X7145, 0X6985, 0X3264, 0X80E5, 0X7145, 0X32C5, 0X70E4, + 0XD086, 0XD086, 0XD086, 0X68E4, 0X4285, 0X04A6, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0405, 0X0547, 0X0547, 0X0547, 0X0506, + 0X6985, 0X9864, 0XD086, 0XD086, 0XA865, 0X9864, 0XD086, 0XD086, 0XD086, + 0XB865, 0X80E5, 0X80A4, 0XC066, 0XD086, 0XD086, 0X58E3, 0X32C5, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0C66, 0X90A4, 0XA865, 0XA865, 0XD086, + 0XD086, 0XD086, 0X6985, 0X0BA5, 0X0405, 0X22C4, 0X0C05, 0X0547, 0X0547, + 0X0C66, 0X7145, 0XC066, 0XD086, 0X70A4, 0X4285, 0X58E3, 0X0506, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X59E5, 0XA865, 0X9044, + 0X9864, 0X4A25, 0X0445, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X1B65, 0X98A5, 0XD086, 0X9044, 0X7145, 0X8044, 0X04A6, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X1BC5, 0X7145, 0X9864, 0XD086, 0X98A5, 0X2305, 0X0506, 0X0506, 0X32C5, + 0X6985, 0X32C5, 0X04A6, 0X0547, 0X0547, 0X0C05, 0X5984, 0XA865, 0X9864, + 0X9864, 0X32C5, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X49C4, 0X59E5, 0X22C4, + 0X70E4, 0X9864, 0X7145, 0X1B65, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0445, 0X0405, 0X0506, 0X0547, 0X0506, + 0X0506, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X2264, + 0X0506, 0X0506, 0X0445, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0506, 0X0405, 0X0445, 0X0547, + 0X0C66, 0X0445, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0506, 0X90A4, 0X80E5, 0X4224, 0X3224, 0X0506, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0405, 0X0547, 0X0506, 0X1A03, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0B64, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X04A6, 0X1BC5, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, 0X0547, + 0X0547, 0X0547, 0X0547, 0X0547, 0X0547}; diff --git a/lib/Adafruit_ILI9341/examples/pictureEmbed/pictureEmbed.ino b/lib/Adafruit_ILI9341/examples/pictureEmbed/pictureEmbed.ino new file mode 100644 index 000000000..5c5475756 --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/pictureEmbed/pictureEmbed.ino @@ -0,0 +1,69 @@ +// ILI9341 example with embedded color bitmaps in sketch. +// WILL NOT FIT ON ARDUINO UNO OR OTHER AVR BOARDS; +// uses large bitmap image stored in array! + +// Options for converting images to the format used here include: +// http://www.rinkydinkelectronics.com/t_imageconverter565.php +// or +// GIMP (https://www.gimp.org/) as follows: +// 1. File -> Export As +// 2. In Export Image dialog, use 'C source code (*.c)' as filetype. +// 3. Press export to get the export options dialog. +// 4. Type the desired variable name into the 'prefixed name' box. +// 5. Uncheck 'GLIB types (guint8*)' +// 6. Check 'Save as RGB565 (16-bit)' +// 7. Press export to save your image. +// Assuming 'image_name' was typed in the 'prefixed name' box of step 4, +// you can have to include the c file, then using the image can be done with: +// tft.drawRGBBitmap(0, 0, image_name.pixel_data, image_name.width, image_name.height); +// See also https://forum.pjrc.com/threads/35575-Export-for-ILI9341_t3-with-GIMP + +#include "SPI.h" +#include +#include "dragon.h" + +// For the Adafruit shield, these are the default. +//#define TFT_DC 9 +//#define TFT_CS 10 + +// Feather 32u4 or M0 with TFT FeatherWing: +#define TFT_DC 10 +#define TFT_CS 9 +// ESP8266: +//#define TFT_DC 15 +//#define TFT_CS 0 +// Other boards (including Feather boards) may have other pinouts; +// see learn.adafruit.com/adafruit-2-4-tft-touch-screen-featherwing/pinouts + +Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); +// If using the breakout, change pins as desired +//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO); + +void setup() { + tft.begin(); +} + +void loop(void) { + for(uint8_t r=0; r<4; r++) { + tft.setRotation(r); + tft.fillScreen(ILI9341_BLACK); + for(uint8_t j=0; j<20; j++) { + tft.drawRGBBitmap( + random(-DRAGON_WIDTH , tft.width()), + random(-DRAGON_HEIGHT, tft.height()), +#if defined(__AVR__) || defined(ESP8266) + dragonBitmap, +#else + // Some non-AVR MCU's have a "flat" memory model and don't + // distinguish between flash and RAM addresses. In this case, + // the RAM-resident-optimized drawRGBBitmap in the ILI9341 + // library can be invoked by forcibly type-converting the + // PROGMEM bitmap pointer to a non-const uint16_t *. + (uint16_t *)dragonBitmap, +#endif + DRAGON_WIDTH, DRAGON_HEIGHT); + delay(1); // Allow ESP8266 to handle watchdog & WiFi stuff + } + delay(3000); + } +} diff --git a/lib/Adafruit_ILI9341/examples/pyportal_boing/.pyportal.test.only b/lib/Adafruit_ILI9341/examples/pyportal_boing/.pyportal.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/lib/Adafruit_ILI9341/examples/pyportal_boing/graphics.h b/lib/Adafruit_ILI9341/examples/pyportal_boing/graphics.h new file mode 100644 index 000000000..8dc1da12f --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/pyportal_boing/graphics.h @@ -0,0 +1,1376 @@ +// Graphics arrays were generated from PNG images using Python and PIL. +// Each is bit-packed into bytes: background is 1bpp, ball is 4bpp. + +const uint8_t background[SCREENHEIGHT][SCREENWIDTH / 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, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X07, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFC, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X08, 0X00, 0X08, 0X00, + 0X08, 0X00, 0X08, 0X00, 0X08, 0X00, 0X04, 0X00, 0X04, 0X00, 0X04, 0X00, + 0X04, 0X00, 0X04, 0X00, 0X02, 0X00, 0X02, 0X00, 0X02, 0X00, 0X02, 0X00, + 0X02, 0X00, 0X03, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X3F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X80, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X40, 0X00, 0X20, 0X00, 0X20, 0X00, 0X10, 0X00, + 0X10, 0X00, 0X08, 0X00, 0X08, 0X00, 0X04, 0X00, 0X02, 0X00, 0X02, 0X00, + 0X01, 0X00, 0X01, 0X00, 0X00, 0X80, 0X00, 0X80, 0X00, 0X40, 0X00, 0X40, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X80, 0X00, 0X40, 0X00, + 0X20, 0X00, 0X20, 0X00, 0X10, 0X00, 0X08, 0X00, 0X08, 0X00, 0X04, 0X00, + 0X02, 0X00, 0X02, 0X00, 0X01, 0X00, 0X00, 0X80, 0X00, 0X80, 0X00, 0X40, + 0X00, 0X20, 0X00, 0X30, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XF8, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X02, 0X00, 0X01, 0X00, 0X00, 0X80, 0X00, 0X40, 0X00, + 0X20, 0X00, 0X10, 0X00, 0X08, 0X00, 0X04, 0X00, 0X02, 0X00, 0X01, 0X00, + 0X00, 0X80, 0X00, 0X40, 0X00, 0X20, 0X00, 0X10, 0X00, 0X08, 0X00, 0X06, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0C, 0X00, 0X02, 0X00, 0X01, + 0X00, 0X00, 0X80, 0X00, 0X40, 0X00, 0X10, 0X00, 0X08, 0X00, 0X04, 0X00, + 0X02, 0X00, 0X01, 0X00, 0X00, 0X40, 0X00, 0X20, 0X00, 0X10, 0X00, 0X08, + 0X00, 0X06, 0X00, 0X01, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X10, + 0X00, 0X04, 0X00, 0X02, 0X00, 0X01, 0X00, 0X00, 0X40, 0X00, 0X20, 0X00, + 0X08, 0X00, 0X04, 0X00, 0X02, 0X00, 0X00, 0X80, 0X00, 0X40, 0X00, 0X10, + 0X00, 0X08, 0X00, 0X04, 0X00, 0X01, 0X00, 0X00, 0X80, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X3F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XE0, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X40, 0X00, 0X10, 0X00, 0X08, + 0X00, 0X02, 0X00, 0X00, 0X80, 0X00, 0X20, 0X00, 0X10, 0X00, 0X04, 0X00, + 0X01, 0X00, 0X00, 0X80, 0X00, 0X20, 0X00, 0X08, 0X00, 0X02, 0X00, 0X01, + 0X00, 0X00, 0X40, 0X00, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X80, + 0X00, 0X20, 0X00, 0X10, 0X00, 0X04, 0X00, 0X01, 0X00, 0X00, 0X40, 0X00, + 0X10, 0X00, 0X04, 0X00, 0X01, 0X00, 0X00, 0X40, 0X00, 0X10, 0X00, 0X04, + 0X00, 0X01, 0X00, 0X00, 0X80, 0X00, 0X20, 0X00, 0X08, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X03, 0X00, 0X00, 0XC0, 0X00, 0X10, 0X00, 0X04, 0X00, 0X01, + 0X00, 0X00, 0X40, 0X00, 0X10, 0X00, 0X04, 0X00, 0X01, 0X00, 0X00, 0X40, + 0X00, 0X10, 0X00, 0X04, 0X00, 0X01, 0X00, 0X00, 0X60, 0X00, 0X18, 0X00, + 0X06, 0X00, 0X00, 0X00, 0X00, 0X00, 0X04, 0X00, 0X01, 0X00, 0X00, 0X20, + 0X00, 0X08, 0X00, 0X02, 0X00, 0X00, 0X80, 0X00, 0X10, 0X00, 0X04, 0X00, + 0X01, 0X00, 0X00, 0X20, 0X00, 0X08, 0X00, 0X02, 0X00, 0X00, 0X80, 0X00, + 0X10, 0X00, 0X04, 0X00, 0X01, 0X00, 0X00, 0X00, 0X00, 0X00, 0X08, 0X00, + 0X02, 0X00, 0X00, 0X40, 0X00, 0X10, 0X00, 0X02, 0X00, 0X00, 0X80, 0X00, + 0X10, 0X00, 0X04, 0X00, 0X01, 0X00, 0X00, 0X20, 0X00, 0X08, 0X00, 0X01, + 0X00, 0X00, 0X40, 0X00, 0X08, 0X00, 0X02, 0X00, 0X00, 0XC0, 0X00, 0X00, + 0X00, 0X00, 0X1F, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XE0, 0X00, 0X00, 0X00, 0X00, 0X60, 0X00, 0X08, 0X00, 0X01, 0X00, + 0X00, 0X20, 0X00, 0X04, 0X00, 0X01, 0X00, 0X00, 0X20, 0X00, 0X04, 0X00, + 0X00, 0X80, 0X00, 0X10, 0X00, 0X04, 0X00, 0X00, 0X80, 0X00, 0X10, 0X00, + 0X02, 0X00, 0X00, 0XC0, 0X00, 0X10, 0X00, 0X00, 0X00, 0X00, 0X80, 0X00, + 0X10, 0X00, 0X02, 0X00, 0X00, 0X40, 0X00, 0X08, 0X00, 0X01, 0X00, 0X00, + 0X20, 0X00, 0X04, 0X00, 0X00, 0X80, 0X00, 0X10, 0X00, 0X02, 0X00, 0X00, + 0X40, 0X00, 0X08, 0X00, 0X01, 0X00, 0X00, 0X20, 0X00, 0X0C, 0X00, 0X00, + 0X00, 0X01, 0X00, 0X00, 0X20, 0X00, 0X04, 0X00, 0X00, 0X80, 0X00, 0X08, + 0X00, 0X01, 0X00, 0X00, 0X20, 0X00, 0X04, 0X00, 0X00, 0X80, 0X00, 0X10, + 0X00, 0X02, 0X00, 0X00, 0X20, 0X00, 0X04, 0X00, 0X00, 0X80, 0X00, 0X10, + 0X00, 0X02, 0X00, 0X00, 0X00, 0X02, 0X00, 0X00, 0X40, 0X00, 0X08, 0X00, + 0X00, 0X80, 0X00, 0X10, 0X00, 0X02, 0X00, 0X00, 0X20, 0X00, 0X04, 0X00, + 0X00, 0X80, 0X00, 0X08, 0X00, 0X01, 0X00, 0X00, 0X20, 0X00, 0X02, 0X00, + 0X00, 0X40, 0X00, 0X08, 0X00, 0X01, 0X80, 0X00, 0X00, 0X04, 0X00, 0X00, + 0X80, 0X00, 0X08, 0X00, 0X01, 0X00, 0X00, 0X10, 0X00, 0X02, 0X00, 0X00, + 0X20, 0X00, 0X04, 0X00, 0X00, 0X80, 0X00, 0X08, 0X00, 0X01, 0X00, 0X00, + 0X10, 0X00, 0X02, 0X00, 0X00, 0X20, 0X00, 0X04, 0X00, 0X00, 0X40, 0X00, + 0X00, 0X18, 0X00, 0X01, 0X00, 0X00, 0X10, 0X00, 0X02, 0X00, 0X00, 0X20, + 0X00, 0X02, 0X00, 0X00, 0X40, 0X00, 0X04, 0X00, 0X00, 0X40, 0X00, 0X08, + 0X00, 0X00, 0X80, 0X00, 0X08, 0X00, 0X01, 0X00, 0X00, 0X10, 0X00, 0X03, + 0X00, 0X00, 0X20, 0X00, 0X00, 0X20, 0X00, 0X02, 0X00, 0X00, 0X20, 0X00, + 0X02, 0X00, 0X00, 0X20, 0X00, 0X04, 0X00, 0X00, 0X40, 0X00, 0X04, 0X00, + 0X00, 0X40, 0X00, 0X04, 0X00, 0X00, 0X80, 0X00, 0X08, 0X00, 0X00, 0X80, + 0X00, 0X08, 0X00, 0X00, 0X80, 0X00, 0X18, 0X00, 0X00, 0X7F, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, + 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 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, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 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 BALLWIDTH 136 +#define BALLHEIGHT 100 + +const uint8_t ball[BALLHEIGHT][BALLWIDTH / 2] = { + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X01, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0XAA, 0XA6, 0X43, 0XDB, 0X00, 0X00, 0X00, 0X00, + 0X01, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XAA, 0XAA, + 0X86, 0X43, 0X32, 0XED, 0X33, 0X33, 0X3D, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0XAA, 0XAA, 0X86, 0X54, 0X33, 0X32, 0XED, 0XCC, + 0XCB, 0X9A, 0XA3, 0X33, 0X33, 0X33, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XAA, 0XAA, + 0X86, 0X55, 0X33, 0X32, 0XFE, 0XDD, 0XCC, 0XCB, 0XA9, 0X88, 0X76, 0XFF, + 0X55, 0X5A, 0X33, 0X33, 0X41, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0XAA, 0XAA, 0X86, 0X55, 0X43, 0X32, 0XFF, 0XED, + 0XDC, 0XCC, 0XCB, 0XA9, 0X88, 0X87, 0X65, 0X54, 0X33, 0XCB, 0XA9, 0X85, + 0X3C, 0X41, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 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, 0XFD, + 0X86, 0X55, 0X54, 0X32, 0XFF, 0XFF, 0XED, 0XDC, 0XCC, 0XBA, 0XA9, 0X88, + 0X87, 0X65, 0X55, 0X43, 0X32, 0X2F, 0XE6, 0X64, 0X32, 0XC9, 0X41, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X10, 0X00, 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, 0X3F, 0XDC, 0XCA, 0X92, 0XFF, 0XFF, + 0XFE, 0XDD, 0XDC, 0XCB, 0XAA, 0XA9, 0X88, 0X87, 0X66, 0X55, 0X54, 0X33, + 0X22, 0XFE, 0XDD, 0XCB, 0X33, 0XFD, 0XA6, 0X41, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X3F, 0XED, 0XCC, 0XA9, 0X88, 0X87, 0XED, 0XDD, 0XCC, 0XCB, 0XAA, + 0XA9, 0X88, 0X88, 0X76, 0X65, 0X54, 0X43, 0X32, 0X2F, 0XEE, 0XDD, 0XCB, + 0XAA, 0X98, 0XDC, 0X96, 0X47, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X33, 0XFD, 0XCC, 0XBA, + 0X98, 0X88, 0X76, 0X65, 0X54, 0XBA, 0XAA, 0XA9, 0X88, 0X88, 0X76, 0X65, + 0X55, 0X44, 0X33, 0X22, 0XFF, 0XED, 0XDC, 0XBB, 0XAA, 0X98, 0X76, 0X5A, + 0X96, 0X48, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X03, 0XFE, 0XDC, 0XCA, 0X98, 0X88, 0X87, 0X66, 0X55, + 0X43, 0X33, 0X22, 0X88, 0X88, 0X76, 0X66, 0X55, 0X54, 0X33, 0X32, 0X2F, + 0XFE, 0XED, 0XDC, 0XBA, 0XAA, 0X98, 0X76, 0X54, 0X32, 0X76, 0X48, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0X3F, + 0XED, 0XCB, 0XA9, 0X88, 0X87, 0X76, 0X65, 0X54, 0X33, 0X32, 0X2F, 0XFF, + 0XE6, 0X66, 0X65, 0X55, 0X43, 0X33, 0X22, 0XFF, 0XEE, 0XDD, 0XCC, 0XBA, + 0XA9, 0X98, 0X76, 0X55, 0X32, 0XFE, 0XDC, 0XF8, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0X3F, 0XED, 0XCC, 0XBA, 0X98, 0X88, + 0X76, 0X66, 0X55, 0X43, 0X33, 0X22, 0XFF, 0XFE, 0XDD, 0XD5, 0X55, 0X44, + 0X33, 0X32, 0X2F, 0XFE, 0XEE, 0XDD, 0XCB, 0XBA, 0XA9, 0X98, 0X76, 0X65, + 0X43, 0X2F, 0XED, 0XC9, 0X88, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X3F, 0XEE, 0XDC, 0XCB, 0XA9, 0X88, 0X87, 0X66, 0X55, 0X44, 0X33, + 0X32, 0X2F, 0XFF, 0XED, 0XDD, 0XCC, 0XCB, 0XB3, 0X33, 0X22, 0XFF, 0XFE, + 0XED, 0XDC, 0XCB, 0XBA, 0XA9, 0X98, 0X76, 0X65, 0X43, 0X2F, 0XED, 0XCB, + 0X98, 0X4F, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X33, 0XFE, 0XED, 0XCB, + 0XA9, 0X88, 0X88, 0X76, 0X65, 0X54, 0X43, 0X33, 0X22, 0XFF, 0XFE, 0XED, + 0XDC, 0XCC, 0XBB, 0XAA, 0XA9, 0X9F, 0XFF, 0XEE, 0XED, 0XDC, 0XCB, 0XBA, + 0XA9, 0X88, 0X76, 0X65, 0X43, 0X32, 0XFE, 0XDC, 0XA9, 0X74, 0XF1, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X03, 0XFE, 0XED, 0XCC, 0XBA, 0X98, 0X88, 0X77, 0X66, + 0X55, 0X44, 0X33, 0X32, 0X2F, 0XFF, 0XEE, 0XDD, 0XDC, 0XCC, 0XBB, 0XAA, + 0X99, 0X98, 0X87, 0XEE, 0XDD, 0XCC, 0XCB, 0XBA, 0XA9, 0X88, 0X77, 0X65, + 0X54, 0X32, 0XFE, 0XDC, 0XC9, 0X86, 0X4F, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0X3F, + 0XEE, 0XDC, 0XBB, 0XA9, 0X88, 0X87, 0X76, 0X65, 0X54, 0X43, 0X33, 0X22, + 0XFF, 0XFE, 0XED, 0XDD, 0XCC, 0XCB, 0XBA, 0XAA, 0X99, 0X88, 0X77, 0X77, + 0X66, 0XCC, 0XBB, 0XAA, 0X99, 0X88, 0X77, 0X65, 0X54, 0X33, 0X2F, 0XED, + 0XCB, 0X98, 0X64, 0XF1, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XA8, 0XEE, 0XED, 0XCB, 0XBA, 0X98, + 0X88, 0X77, 0X66, 0X55, 0X44, 0X33, 0X33, 0X22, 0XFF, 0XEE, 0XDD, 0XDC, + 0XCC, 0XBB, 0XAA, 0XA9, 0X99, 0X88, 0X77, 0X76, 0X66, 0X55, 0XBB, 0XAA, + 0X99, 0X88, 0X77, 0X65, 0X54, 0X33, 0X2F, 0XEE, 0XCC, 0XA9, 0X85, 0X2E, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X0A, 0X87, 0X76, 0X5B, 0XBA, 0X98, 0X88, 0X87, 0X66, 0X65, 0X54, + 0X43, 0X33, 0X32, 0X2F, 0XFF, 0XED, 0XDD, 0XCC, 0XCC, 0XBB, 0XAA, 0XA9, + 0X98, 0X87, 0X77, 0X76, 0X65, 0X55, 0X44, 0X33, 0X99, 0X88, 0X77, 0X66, + 0X54, 0X43, 0X22, 0XFE, 0XDC, 0XCA, 0X97, 0X52, 0XE1, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0A, 0X87, 0X65, + 0X44, 0X32, 0X88, 0X87, 0X76, 0X65, 0X54, 0X44, 0X33, 0X33, 0X22, 0XFF, + 0XFE, 0XED, 0XDD, 0XCC, 0XCB, 0XBA, 0XAA, 0X99, 0X98, 0X87, 0X77, 0X66, + 0X65, 0X54, 0X44, 0X33, 0X22, 0XFF, 0X77, 0X66, 0X55, 0X43, 0X32, 0XFE, + 0XED, 0XCB, 0XA9, 0X75, 0X2E, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0XA8, 0X77, 0X65, 0X43, 0X2F, 0XFF, 0XEE, + 0X66, 0X55, 0X44, 0X43, 0X33, 0X32, 0X2F, 0XFF, 0XEE, 0XDD, 0XDC, 0XCC, + 0XBB, 0XAA, 0XAA, 0X99, 0X88, 0X77, 0X77, 0X66, 0X55, 0X54, 0X43, 0X32, + 0X22, 0XFF, 0XEE, 0X66, 0X55, 0X43, 0X32, 0X2F, 0XEE, 0XCC, 0XB9, 0X86, + 0X52, 0XE1, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0XA8, 0X76, 0X54, 0X43, 0X2F, 0XFE, 0XED, 0XDC, 0XC4, 0X44, 0X33, + 0X33, 0X22, 0XFF, 0XFE, 0XED, 0XDD, 0XCC, 0XCB, 0XBB, 0XAA, 0XA9, 0X99, + 0X88, 0X77, 0X76, 0X66, 0X55, 0X54, 0X43, 0X32, 0X22, 0XFF, 0XEE, 0XDD, + 0XCC, 0X44, 0X33, 0X2F, 0XEE, 0XDC, 0XBA, 0X98, 0X64, 0XF1, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0A, 0X87, 0X65, 0X54, + 0X32, 0XFF, 0XFE, 0XED, 0XCC, 0XBB, 0XBA, 0X33, 0X32, 0X2F, 0XFF, 0XEE, + 0XDD, 0XDC, 0XCC, 0XCB, 0XBA, 0XAA, 0X99, 0X98, 0X87, 0X77, 0X76, 0X66, + 0X55, 0X44, 0X43, 0X32, 0X2F, 0XFF, 0XEE, 0XDD, 0XCC, 0XBB, 0XAA, 0X22, + 0XFE, 0XEC, 0XCB, 0XA9, 0X76, 0X4F, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0XA8, 0X77, 0X65, 0X43, 0X22, 0XFF, 0XEE, 0XDD, + 0XCC, 0XBB, 0XBA, 0XAA, 0X99, 0XFF, 0XFE, 0XEE, 0XDD, 0XDC, 0XCC, 0XBB, + 0XBA, 0XAA, 0X99, 0X98, 0X87, 0X77, 0X66, 0X65, 0X55, 0X44, 0X33, 0X32, + 0X2F, 0XFE, 0XEE, 0XDD, 0XCC, 0XBB, 0XAA, 0X99, 0XFE, 0XED, 0XCC, 0XBA, + 0X97, 0X64, 0XF1, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0XA8, 0X76, 0X54, 0X43, 0X2F, 0XFE, 0XEE, 0XDC, 0XCB, 0XBB, 0XAA, 0XA9, + 0X98, 0X88, 0XEE, 0XED, 0XDD, 0XCC, 0XCB, 0XBB, 0XAA, 0XA9, 0X99, 0X88, + 0X77, 0X77, 0X66, 0X65, 0X55, 0X44, 0X33, 0X22, 0X2F, 0XFE, 0XED, 0XDD, + 0XCC, 0XBB, 0XAA, 0X99, 0X88, 0X77, 0XCC, 0XBA, 0X98, 0X75, 0X4F, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X0A, 0X87, 0X76, 0X54, 0X32, + 0X2F, 0XFE, 0XED, 0XDC, 0XCB, 0XBB, 0XAA, 0XA9, 0X98, 0X88, 0X77, 0X66, + 0X65, 0XCC, 0XCB, 0XBA, 0XAA, 0XA9, 0X99, 0X88, 0X77, 0X76, 0X66, 0X55, + 0X54, 0X44, 0X33, 0X22, 0X2F, 0XFE, 0XED, 0XDD, 0XCC, 0XBB, 0XAA, 0X99, + 0X88, 0X77, 0X65, 0X54, 0XA9, 0X86, 0X52, 0XE1, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X0A, 0X87, 0X65, 0X44, 0X32, 0XFF, 0XEE, 0XDD, 0XCC, + 0XBB, 0XBA, 0XAA, 0X99, 0X88, 0X87, 0X77, 0X66, 0X65, 0X55, 0XBB, 0XBA, + 0XAA, 0X99, 0X98, 0X87, 0X77, 0X76, 0X66, 0X55, 0X54, 0X43, 0X33, 0X22, + 0X2F, 0XFE, 0XED, 0XDD, 0XCC, 0XBB, 0XAA, 0X99, 0X88, 0X77, 0X65, 0X54, + 0X33, 0X27, 0X65, 0X2E, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XA8, + 0X76, 0X55, 0X43, 0X22, 0XFF, 0XEE, 0XDD, 0XCC, 0XBB, 0XBA, 0XAA, 0X99, + 0X88, 0X87, 0X76, 0X66, 0X55, 0X55, 0X44, 0X3A, 0XAA, 0X99, 0X98, 0X87, + 0X77, 0X66, 0X66, 0X55, 0X54, 0X43, 0X33, 0X22, 0XFF, 0XFE, 0XED, 0XDD, + 0XCC, 0XBB, 0XAA, 0X99, 0X88, 0X77, 0X65, 0X54, 0X33, 0X2F, 0XED, 0XC2, + 0XE1, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0XA8, 0X76, 0X54, 0X32, 0X2F, + 0XFE, 0XED, 0XDC, 0XCB, 0XBB, 0XAA, 0XA9, 0X98, 0X88, 0X77, 0X76, 0X66, + 0X55, 0X54, 0X44, 0X33, 0X32, 0X99, 0X88, 0X77, 0X77, 0X66, 0X65, 0X55, + 0X44, 0X43, 0X32, 0X22, 0XFF, 0XFE, 0XED, 0XDD, 0XCC, 0XBB, 0XAA, 0X99, + 0X88, 0X77, 0X66, 0X55, 0X43, 0X2F, 0XED, 0XCB, 0X97, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, + 0X00, 0X00, 0X0A, 0X87, 0X65, 0X54, 0X32, 0X2F, 0XEE, 0XED, 0XDC, 0XCB, + 0XBB, 0XAA, 0XA9, 0X98, 0X88, 0X77, 0X66, 0X65, 0X55, 0X54, 0X43, 0X33, + 0X32, 0X22, 0XFF, 0XE7, 0X76, 0X66, 0X65, 0X55, 0X44, 0X33, 0X32, 0X22, + 0XFF, 0XFE, 0XED, 0XDC, 0XCC, 0XBB, 0XAA, 0X99, 0X88, 0X77, 0X76, 0X55, + 0X43, 0X32, 0XFE, 0XDC, 0X97, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0XA8, 0X76, + 0X55, 0X43, 0X22, 0XFF, 0XEE, 0XDD, 0XCC, 0XBB, 0XBA, 0XAA, 0X99, 0X88, + 0X87, 0X77, 0X66, 0X65, 0X55, 0X44, 0X43, 0X33, 0X22, 0X22, 0XFF, 0XEE, + 0XED, 0X66, 0X55, 0X55, 0X44, 0X33, 0X32, 0X22, 0XFF, 0XFE, 0XED, 0XDC, + 0XCC, 0XBB, 0XAA, 0X99, 0X88, 0X77, 0X76, 0X55, 0X43, 0X32, 0XFE, 0XDC, + 0XB9, 0X71, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0XA8, 0X76, 0X54, 0X32, 0X22, 0XFE, + 0XEE, 0XDD, 0XCC, 0XBB, 0XBA, 0XAA, 0X99, 0X88, 0X87, 0X76, 0X66, 0X55, + 0X55, 0X44, 0X33, 0X33, 0X22, 0X2F, 0XFE, 0XEE, 0XED, 0XDD, 0XC5, 0X54, + 0X44, 0X33, 0X32, 0X22, 0XFF, 0XEE, 0XED, 0XDC, 0XCC, 0XBB, 0XAA, 0XA9, + 0X88, 0X77, 0X76, 0X55, 0X43, 0X32, 0XFE, 0XDC, 0XB9, 0X71, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, + 0X00, 0X0A, 0X87, 0X65, 0X54, 0X32, 0X2F, 0XEE, 0XED, 0XDC, 0XCB, 0XBB, + 0XAA, 0XA9, 0X98, 0X88, 0X77, 0X76, 0X66, 0X55, 0X54, 0X44, 0X33, 0X32, + 0X22, 0X2F, 0XFE, 0XEE, 0XDD, 0XDD, 0XCC, 0XC4, 0X43, 0X33, 0X22, 0X2F, + 0XFF, 0XEE, 0XDD, 0XDC, 0XCC, 0XBB, 0XAA, 0XA9, 0X98, 0X87, 0X76, 0X55, + 0X44, 0X32, 0XFE, 0XDD, 0XCA, 0X81, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X0A, 0X87, 0X65, + 0X43, 0X22, 0X2F, 0XEE, 0XDD, 0XDC, 0XCB, 0XBB, 0XAA, 0XA9, 0X98, 0X88, + 0X77, 0X66, 0X65, 0X55, 0X54, 0X44, 0X33, 0X32, 0X22, 0XFF, 0XEE, 0XEE, + 0XDD, 0XDC, 0XCC, 0XCB, 0XBA, 0X33, 0X22, 0X2F, 0XFF, 0XEE, 0XDD, 0XDC, + 0XCC, 0XBB, 0XAA, 0XA9, 0X98, 0X87, 0X76, 0X55, 0X44, 0X33, 0X2F, 0XED, + 0XCB, 0X97, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X10, 0X00, 0X00, 0X3F, 0XED, 0X55, 0X43, 0X22, 0XFE, 0XEE, + 0XDD, 0XCC, 0XBB, 0XBA, 0XAA, 0X99, 0X88, 0X87, 0X77, 0X66, 0X65, 0X55, + 0X44, 0X43, 0X33, 0X32, 0X22, 0XFF, 0XEE, 0XED, 0XDD, 0XDC, 0XCC, 0XBB, + 0XBA, 0XAA, 0X99, 0X98, 0XFF, 0XEE, 0XDD, 0XDC, 0XCC, 0XBB, 0XAA, 0XA9, + 0X98, 0X87, 0X76, 0X65, 0X54, 0X33, 0X2F, 0XED, 0XCB, 0X97, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, + 0X00, 0X3F, 0XED, 0XCB, 0XA2, 0X22, 0XFE, 0XED, 0XDD, 0XCC, 0XBB, 0XBA, + 0XAA, 0X99, 0X88, 0X87, 0X76, 0X66, 0X65, 0X55, 0X44, 0X43, 0X33, 0X22, + 0X2F, 0XFF, 0XEE, 0XED, 0XDD, 0XCC, 0XCC, 0XBB, 0XAA, 0XAA, 0X99, 0X98, + 0X88, 0XEE, 0XDD, 0XDC, 0XCC, 0XBB, 0XAA, 0XA9, 0X98, 0X87, 0X76, 0X65, + 0X54, 0X33, 0X2F, 0XED, 0XDC, 0XA8, 0X51, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X3F, 0XED, 0XCB, + 0XA9, 0X98, 0XEE, 0XED, 0XDC, 0XCB, 0XBB, 0XAA, 0XA9, 0X98, 0X88, 0X77, + 0X76, 0X66, 0X55, 0X54, 0X44, 0X33, 0X33, 0X22, 0X2F, 0XFE, 0XEE, 0XED, + 0XDD, 0XCC, 0XCC, 0XBB, 0XAA, 0XA9, 0X99, 0X88, 0X88, 0X77, 0X6D, 0XDC, + 0XCC, 0XBB, 0XAA, 0XA9, 0X98, 0X87, 0X76, 0X65, 0X54, 0X33, 0X2F, 0XFE, + 0XDC, 0XB9, 0X71, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X10, 0X00, 0X3F, 0XED, 0XCB, 0XA9, 0X98, 0X77, 0XDD, + 0XDC, 0XCB, 0XBB, 0XAA, 0XA9, 0X98, 0X88, 0X77, 0X66, 0X66, 0X55, 0X54, + 0X44, 0X33, 0X32, 0X22, 0XFF, 0XFE, 0XEE, 0XDD, 0XDD, 0XCC, 0XCB, 0XBB, + 0XAA, 0XA9, 0X99, 0X88, 0X87, 0X77, 0X66, 0X65, 0XCC, 0XBB, 0XAA, 0XA9, + 0X98, 0X87, 0X77, 0X65, 0X54, 0X43, 0X3F, 0XFE, 0XDC, 0XBA, 0X81, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, + 0X00, 0X3F, 0XDC, 0XBB, 0XA9, 0X98, 0X77, 0X66, 0X55, 0X4B, 0XBA, 0XAA, + 0X99, 0X88, 0X87, 0X77, 0X66, 0X65, 0X55, 0X44, 0X43, 0X33, 0X32, 0X22, + 0XFF, 0XEE, 0XEE, 0XDD, 0XDC, 0XCC, 0XCB, 0XBB, 0XAA, 0XA9, 0X99, 0X88, + 0X87, 0X76, 0X66, 0X65, 0X55, 0X44, 0X3A, 0XA9, 0X98, 0X87, 0X77, 0X65, + 0X54, 0X43, 0X32, 0XFE, 0XDD, 0XBA, 0X85, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X03, 0XFE, 0XDC, 0XBA, + 0XA9, 0X98, 0X77, 0X66, 0X55, 0X44, 0X4A, 0XAA, 0X99, 0X88, 0X87, 0X76, + 0X66, 0X65, 0X55, 0X44, 0X43, 0X33, 0X22, 0X2F, 0XFF, 0XEE, 0XED, 0XDD, + 0XDC, 0XCC, 0XCB, 0XBA, 0XAA, 0XA9, 0X99, 0X88, 0X87, 0X76, 0X66, 0X55, + 0X55, 0X44, 0X33, 0X39, 0X98, 0X87, 0X77, 0X65, 0X54, 0X43, 0X32, 0XFF, + 0XDD, 0XCB, 0X95, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X03, 0XFE, 0XDC, 0XBA, 0X99, 0X88, 0X77, 0X66, + 0X55, 0X44, 0X43, 0X32, 0X2F, 0X88, 0X77, 0X76, 0X66, 0X55, 0X54, 0X44, + 0X43, 0X33, 0X22, 0X2F, 0XFF, 0XEE, 0XED, 0XDD, 0XCC, 0XCC, 0XBB, 0XBA, + 0XAA, 0X99, 0X98, 0X88, 0X87, 0X76, 0X66, 0X55, 0X54, 0X44, 0X33, 0X32, + 0X2F, 0XFE, 0X77, 0X66, 0X55, 0X43, 0X32, 0XFF, 0XED, 0XCB, 0XA8, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X03, 0XFE, 0XDC, 0XBA, 0X99, 0X87, 0X76, 0X65, 0X55, 0X44, 0X43, 0X32, + 0X2F, 0XFF, 0X77, 0X66, 0X66, 0X55, 0X54, 0X44, 0X33, 0X33, 0X22, 0XFF, + 0XFE, 0XEE, 0XED, 0XDD, 0XCC, 0XCC, 0XBB, 0XBA, 0XAA, 0X99, 0X98, 0X88, + 0X77, 0X76, 0X66, 0X55, 0X54, 0X43, 0X33, 0X32, 0X2F, 0XFE, 0XEE, 0X66, + 0X55, 0X44, 0X33, 0XFF, 0XED, 0XDB, 0XA8, 0X51, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X03, 0XFE, 0XDC, 0XBA, + 0X99, 0X87, 0X76, 0X65, 0X54, 0X44, 0X33, 0X32, 0X2F, 0XFF, 0XEE, 0XD6, + 0X65, 0X55, 0X44, 0X44, 0X33, 0X32, 0X22, 0XFF, 0XFE, 0XEE, 0XDD, 0XDD, + 0XCC, 0XCB, 0XBB, 0XAA, 0XAA, 0X99, 0X98, 0X88, 0X77, 0X66, 0X66, 0X55, + 0X54, 0X43, 0X33, 0X22, 0X2F, 0XFE, 0XEE, 0XDD, 0XCC, 0X44, 0X33, 0X2F, + 0XED, 0XDC, 0XB9, 0X51, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X03, 0XFE, 0XDC, 0XBA, 0X99, 0X87, 0X76, 0X65, + 0X54, 0X44, 0X33, 0X22, 0X2F, 0XFF, 0XEE, 0XDD, 0XDC, 0XCC, 0XB4, 0X43, + 0X33, 0X32, 0X2F, 0XFF, 0XFE, 0XEE, 0XDD, 0XDC, 0XCC, 0XCB, 0XBB, 0XAA, + 0XA9, 0X99, 0X88, 0X88, 0X77, 0X66, 0X65, 0X55, 0X54, 0X43, 0X33, 0X22, + 0XFF, 0XFE, 0XEE, 0XDD, 0XCC, 0XBB, 0XAA, 0X2F, 0XFD, 0XDC, 0XBA, 0X85, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X03, 0XFD, 0XCB, 0XBA, 0X99, 0X87, 0X76, 0X65, 0X54, 0X44, 0X33, 0X22, + 0XFF, 0XFE, 0XEE, 0XDD, 0XDC, 0XCC, 0XBB, 0XBA, 0X33, 0X22, 0X2F, 0XFF, + 0XEE, 0XEE, 0XDD, 0XDC, 0XCC, 0XCB, 0XBB, 0XAA, 0XA9, 0X99, 0X88, 0X87, + 0X77, 0X66, 0X65, 0X55, 0X44, 0X43, 0X33, 0X22, 0XFF, 0XEE, 0XED, 0XDD, + 0XCC, 0XBB, 0XAA, 0X98, 0X8E, 0XDD, 0XBA, 0X85, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X03, 0XFD, 0XCB, 0XAA, + 0X99, 0X87, 0X76, 0X65, 0X54, 0X44, 0X33, 0X22, 0XFF, 0XFE, 0XED, 0XDD, + 0XDC, 0XCB, 0XBB, 0XBA, 0XAA, 0X22, 0XFF, 0XFF, 0XEE, 0XED, 0XDD, 0XDC, + 0XCC, 0XBB, 0XBA, 0XAA, 0XA9, 0X99, 0X88, 0X87, 0X76, 0X66, 0X65, 0X55, + 0X44, 0X33, 0X33, 0X22, 0XFF, 0XEE, 0XED, 0XDC, 0XCC, 0XBB, 0XAA, 0X98, + 0X87, 0X66, 0XCB, 0XA8, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X03, 0XFD, 0XCB, 0XAA, 0X98, 0X87, 0X76, 0X65, + 0X54, 0X43, 0X33, 0X22, 0XFF, 0XFE, 0XED, 0XDD, 0XCC, 0XCB, 0XBB, 0XAA, + 0XAA, 0X99, 0X8F, 0XFE, 0XEE, 0XED, 0XDD, 0XCC, 0XCC, 0XBB, 0XBA, 0XAA, + 0X99, 0X99, 0X88, 0X87, 0X76, 0X66, 0X55, 0X55, 0X44, 0X33, 0X32, 0X22, + 0XFF, 0XEE, 0XED, 0XDC, 0XCB, 0XBA, 0XAA, 0X98, 0X87, 0X66, 0X54, 0X3F, + 0X51, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X03, 0XFD, 0XCB, 0XAA, 0X98, 0X87, 0X66, 0X55, 0X54, 0X43, 0X32, 0X22, + 0XFF, 0XFE, 0XED, 0XDD, 0XCC, 0XCB, 0XBB, 0XAA, 0XA9, 0X99, 0X88, 0X87, + 0XEE, 0XDD, 0XDD, 0XCC, 0XCC, 0XBB, 0XBA, 0XAA, 0X99, 0X98, 0X88, 0X87, + 0X76, 0X66, 0X55, 0X54, 0X44, 0X33, 0X32, 0X2F, 0XFF, 0XEE, 0XED, 0XDC, + 0XCB, 0XBA, 0XA9, 0X98, 0X87, 0X66, 0X54, 0X3F, 0XC1, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X32, 0XFD, 0XCB, 0XAA, + 0X98, 0X87, 0X66, 0X55, 0X44, 0X43, 0X32, 0X22, 0XFF, 0XEE, 0XED, 0XDD, + 0XCC, 0XCB, 0XBB, 0XAA, 0XA9, 0X98, 0X88, 0X87, 0X77, 0X6D, 0XDC, 0XCC, + 0XCB, 0XBB, 0XBA, 0XAA, 0X99, 0X98, 0X88, 0X77, 0X76, 0X66, 0X55, 0X54, + 0X44, 0X33, 0X32, 0X2F, 0XFF, 0XEE, 0XDD, 0XDC, 0XCB, 0XBA, 0XA9, 0X98, + 0X77, 0X66, 0X54, 0X3F, 0XC1, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X3F, 0XED, 0XCB, 0XAA, 0X98, 0X87, 0X66, 0X55, + 0X44, 0X43, 0X32, 0X22, 0XFF, 0XEE, 0XDD, 0XDD, 0XCC, 0XBB, 0XBB, 0XAA, + 0XA9, 0X98, 0X88, 0X77, 0X77, 0X66, 0X65, 0X55, 0X54, 0XBB, 0XAA, 0XAA, + 0X99, 0X98, 0X88, 0X77, 0X66, 0X66, 0X55, 0X54, 0X44, 0X33, 0X32, 0X2F, + 0XFF, 0XEE, 0XDD, 0XCC, 0XCB, 0XBA, 0XA9, 0X88, 0X77, 0X65, 0X44, 0X3F, + 0XC1, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, + 0X3F, 0XDC, 0XBB, 0XA9, 0X98, 0X87, 0X66, 0X55, 0X44, 0X43, 0X32, 0X2F, + 0XFF, 0XEE, 0XDD, 0XDC, 0XCC, 0XBB, 0XBA, 0XAA, 0XA9, 0X98, 0X88, 0X77, + 0X76, 0X66, 0X65, 0X55, 0X54, 0X44, 0X3A, 0XA9, 0X99, 0X98, 0X88, 0X77, + 0X66, 0X65, 0X55, 0X54, 0X43, 0X33, 0X32, 0X2F, 0XFE, 0XEE, 0XDD, 0XCC, + 0XCB, 0XBA, 0XA9, 0X88, 0X76, 0X65, 0X43, 0X2F, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X3F, 0XDC, 0XBA, 0XA9, + 0X98, 0X77, 0X66, 0X55, 0X44, 0X33, 0X32, 0X2F, 0XFF, 0XEE, 0XDD, 0XDC, + 0XCC, 0XBB, 0XBA, 0XAA, 0X99, 0X98, 0X88, 0X77, 0X76, 0X66, 0X55, 0X55, + 0X44, 0X44, 0X33, 0X32, 0X99, 0X88, 0X88, 0X77, 0X66, 0X65, 0X55, 0X44, + 0X43, 0X33, 0X22, 0X2F, 0XFE, 0XEE, 0XDD, 0XCC, 0XCB, 0XBA, 0XA9, 0X88, + 0X76, 0X65, 0X43, 0XFE, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X10, 0XAF, 0XDC, 0XBA, 0XA9, 0X88, 0X77, 0X66, 0X55, + 0X44, 0X33, 0X22, 0X2F, 0XFF, 0XEE, 0XDD, 0XDC, 0XCB, 0XBB, 0XBA, 0XAA, + 0X99, 0X88, 0X88, 0X77, 0X76, 0X66, 0X55, 0X55, 0X44, 0X43, 0X33, 0X32, + 0X22, 0XF8, 0X87, 0X77, 0X66, 0X65, 0X55, 0X44, 0X43, 0X33, 0X22, 0XFF, + 0XFE, 0XEE, 0XDD, 0XCC, 0XBB, 0XAA, 0XA9, 0X88, 0X76, 0X65, 0X43, 0XFC, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, + 0XA8, 0X65, 0XBA, 0XA9, 0X88, 0X76, 0X65, 0X55, 0X44, 0X33, 0X22, 0X2F, + 0XFE, 0XEE, 0XDD, 0XDC, 0XCB, 0XBB, 0XAA, 0XAA, 0X99, 0X88, 0X87, 0X77, + 0X76, 0X66, 0X55, 0X54, 0X44, 0X43, 0X33, 0X22, 0X22, 0XFF, 0XF7, 0X76, + 0X66, 0X65, 0X55, 0X44, 0X43, 0X33, 0X22, 0XFF, 0XFE, 0XED, 0XDD, 0XCC, + 0XBB, 0XAA, 0X99, 0X88, 0X76, 0X65, 0X43, 0XFC, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X9A, 0X65, 0X43, 0X39, + 0X88, 0X76, 0X65, 0X54, 0X44, 0X33, 0X22, 0X2F, 0XFE, 0XED, 0XDD, 0XDC, + 0XCB, 0XBB, 0XAA, 0XA9, 0X99, 0X88, 0X87, 0X77, 0X66, 0X66, 0X55, 0X54, + 0X44, 0X33, 0X33, 0X22, 0X2F, 0XFF, 0XFE, 0XED, 0X66, 0X55, 0X55, 0X44, + 0X33, 0X33, 0X22, 0XFF, 0XFE, 0XED, 0XDC, 0XCC, 0XBB, 0XAA, 0X99, 0X87, + 0X76, 0X65, 0X43, 0XFC, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X10, 0X0A, 0X86, 0X53, 0X32, 0XFF, 0X76, 0X65, 0X54, + 0X43, 0X33, 0X22, 0X2F, 0XFE, 0XED, 0XDD, 0XCC, 0XCB, 0XBB, 0XAA, 0XA9, + 0X98, 0X88, 0X87, 0X77, 0X66, 0X65, 0X55, 0X54, 0X44, 0X33, 0X32, 0X22, + 0X2F, 0XFF, 0XEE, 0XED, 0XDD, 0XC5, 0X54, 0X44, 0X33, 0X32, 0X22, 0XFF, + 0XFE, 0XED, 0XDC, 0XCC, 0XBB, 0XAA, 0X99, 0X87, 0X76, 0X54, 0X43, 0XFC, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, + 0X0A, 0X86, 0X54, 0X33, 0XFF, 0XED, 0XD5, 0X54, 0X43, 0X32, 0X22, 0XFF, + 0XFE, 0XED, 0XDD, 0XCC, 0XBB, 0XBB, 0XAA, 0XA9, 0X98, 0X88, 0X77, 0X77, + 0X66, 0X65, 0X55, 0X44, 0X44, 0X33, 0X32, 0X22, 0XFF, 0XFF, 0XEE, 0XDD, + 0XDD, 0XCC, 0XCB, 0XBB, 0XA3, 0X32, 0X2F, 0XFF, 0XEE, 0XED, 0XDC, 0XCC, + 0XBB, 0XAA, 0X99, 0X87, 0X76, 0X54, 0X32, 0XF1, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0XA6, 0X54, 0X33, + 0X2F, 0XED, 0XDC, 0XCB, 0XB3, 0X32, 0X22, 0XFF, 0XEE, 0XED, 0XDD, 0XCC, + 0XBB, 0XBA, 0XAA, 0XA9, 0X98, 0X88, 0X77, 0X76, 0X66, 0X65, 0X55, 0X44, + 0X43, 0X33, 0X32, 0X22, 0XFF, 0XFE, 0XEE, 0XDD, 0XDC, 0XCC, 0XCB, 0XBB, + 0XAA, 0XA2, 0X2F, 0XFF, 0XEE, 0XDD, 0XDC, 0XCC, 0XBB, 0XAA, 0X99, 0X87, + 0X76, 0X54, 0X32, 0XF1, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X10, 0X00, 0XA8, 0X65, 0X33, 0X2F, 0XFD, 0XDC, 0XCB, + 0XBA, 0XA9, 0X22, 0XFF, 0XEE, 0XDD, 0XDD, 0XCC, 0XBB, 0XBA, 0XAA, 0X99, + 0X98, 0X88, 0X77, 0X76, 0X66, 0X55, 0X54, 0X44, 0X43, 0X33, 0X22, 0X22, + 0XFF, 0XFE, 0XED, 0XDD, 0XDC, 0XCC, 0XBB, 0XBA, 0XAA, 0XA9, 0X98, 0XFF, + 0XEE, 0XDD, 0XCC, 0XCB, 0XBA, 0XAA, 0X99, 0X87, 0X76, 0X54, 0X32, 0XF1, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, + 0X00, 0XA8, 0X65, 0X43, 0X2F, 0XFE, 0XDD, 0XCB, 0XBA, 0XA9, 0X99, 0X88, + 0XEE, 0XDD, 0XDC, 0XCC, 0XBB, 0XBA, 0XAA, 0X99, 0X88, 0X88, 0X77, 0X76, + 0X66, 0X55, 0X54, 0X44, 0X33, 0X33, 0X22, 0X2F, 0XFF, 0XEE, 0XED, 0XDD, + 0XCC, 0XCC, 0XBB, 0XBA, 0XAA, 0X99, 0X98, 0X88, 0X77, 0XDD, 0XCC, 0XCB, + 0XBA, 0XA9, 0X98, 0X87, 0X66, 0X54, 0X32, 0XF1, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X0A, 0X65, 0X43, + 0X3F, 0XFE, 0XDD, 0XCC, 0XBB, 0XA9, 0X99, 0X88, 0X77, 0X6D, 0XDC, 0XCB, + 0XBB, 0XBA, 0XAA, 0X99, 0X88, 0X87, 0X77, 0X66, 0X66, 0X55, 0X54, 0X44, + 0X33, 0X32, 0X22, 0X2F, 0XFF, 0XEE, 0XED, 0XDD, 0XCC, 0XCB, 0XBB, 0XAA, + 0XAA, 0X99, 0X88, 0X87, 0X76, 0X66, 0X5C, 0XCB, 0XBA, 0XA9, 0X98, 0X77, + 0X66, 0X54, 0X32, 0XF1, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X10, 0X00, 0X0A, 0X86, 0X53, 0X32, 0XFE, 0XDD, 0XCC, + 0XBB, 0XA9, 0X99, 0X88, 0X77, 0X66, 0X65, 0X54, 0XBB, 0XAA, 0XAA, 0X99, + 0X88, 0X87, 0X77, 0X66, 0X65, 0X55, 0X44, 0X44, 0X33, 0X32, 0X22, 0XFF, + 0XFE, 0XEE, 0XDD, 0XDD, 0XCC, 0XCB, 0XBB, 0XAA, 0XA9, 0X99, 0X88, 0X87, + 0X76, 0X66, 0X55, 0X54, 0X4A, 0XA9, 0X98, 0X77, 0X65, 0X44, 0X3F, 0XC1, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, + 0X00, 0X0A, 0X96, 0X54, 0X32, 0XFF, 0XDD, 0XCC, 0XBB, 0XA9, 0X99, 0X88, + 0X77, 0X66, 0X65, 0X54, 0X44, 0X3A, 0XA9, 0X99, 0X88, 0X87, 0X77, 0X66, + 0X65, 0X55, 0X44, 0X43, 0X33, 0X32, 0X22, 0XFF, 0XFE, 0XEE, 0XDD, 0XDC, + 0XCC, 0XBB, 0XBA, 0XAA, 0XA9, 0X98, 0X88, 0X77, 0X66, 0X65, 0X55, 0X44, + 0X33, 0X39, 0X98, 0X77, 0X65, 0X43, 0X2F, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X0A, 0X96, 0X54, + 0X33, 0XFF, 0XED, 0XDC, 0XBB, 0XAA, 0X99, 0X88, 0X77, 0X66, 0X65, 0X54, + 0X44, 0X33, 0X39, 0X98, 0X88, 0X87, 0X77, 0X66, 0X65, 0X55, 0X44, 0X43, + 0X33, 0X22, 0X22, 0XFF, 0XEE, 0XED, 0XDD, 0XDC, 0XCC, 0XBB, 0XBA, 0XAA, + 0X99, 0X98, 0X88, 0X77, 0X66, 0X65, 0X55, 0X44, 0X33, 0X22, 0X2F, 0XEE, + 0X65, 0X43, 0X2F, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0XA8, 0X64, 0X33, 0X2F, 0XED, 0XDC, + 0XBB, 0XAA, 0X99, 0X88, 0X77, 0X66, 0X65, 0X54, 0X44, 0X33, 0X32, 0X2F, + 0X88, 0X77, 0X76, 0X66, 0X65, 0X54, 0X44, 0X43, 0X33, 0X22, 0X2F, 0XFF, + 0XEE, 0XED, 0XDD, 0XCC, 0XCB, 0XBB, 0XAA, 0XAA, 0X99, 0X88, 0X87, 0X76, + 0X66, 0X55, 0X54, 0X43, 0X33, 0X22, 0XFE, 0XED, 0XDC, 0X43, 0X2F, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, + 0X00, 0X00, 0XA9, 0X65, 0X43, 0X2F, 0XED, 0XDC, 0XBB, 0XAA, 0X99, 0X88, + 0X77, 0X66, 0X65, 0X54, 0X44, 0X33, 0X32, 0X2F, 0XFF, 0XEE, 0XED, 0X66, + 0X55, 0X54, 0X44, 0X33, 0X33, 0X22, 0X2F, 0XFE, 0XEE, 0XDD, 0XDD, 0XCC, + 0XCB, 0XBB, 0XAA, 0XA9, 0X99, 0X88, 0X87, 0X76, 0X66, 0X55, 0X54, 0X43, + 0X32, 0X22, 0XFE, 0XED, 0XCB, 0XBA, 0X98, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0XA9, 0X65, + 0X43, 0X2F, 0XED, 0XDC, 0XCB, 0XBA, 0X99, 0X88, 0X77, 0X66, 0X65, 0X54, + 0X44, 0X33, 0X32, 0X2F, 0XFF, 0XEE, 0XED, 0XDD, 0X55, 0X44, 0X44, 0X33, + 0X32, 0X22, 0X2F, 0XFE, 0XEE, 0XDD, 0XDC, 0XCC, 0XBB, 0XBB, 0XAA, 0XA9, + 0X98, 0X88, 0X77, 0X66, 0X65, 0X55, 0X44, 0X33, 0X32, 0X2F, 0XEE, 0XDD, + 0XCB, 0XA9, 0X81, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0X0A, 0X86, 0X43, 0X32, 0XFE, 0XDC, + 0XCB, 0XBA, 0X99, 0X98, 0X77, 0X66, 0X65, 0X54, 0X44, 0X33, 0X22, 0X2F, + 0XFF, 0XEE, 0XDD, 0XDD, 0XCC, 0XB4, 0X43, 0X33, 0X32, 0X22, 0XFF, 0XEE, + 0XEE, 0XDD, 0XDC, 0XCC, 0XBB, 0XBA, 0XAA, 0X99, 0X98, 0X88, 0X77, 0X66, + 0X65, 0X55, 0X44, 0X33, 0X22, 0XFF, 0XEE, 0XDC, 0XBB, 0XA9, 0X81, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, + 0X00, 0X00, 0X0A, 0X96, 0X54, 0X32, 0XFE, 0XDD, 0XCB, 0XBA, 0X99, 0X98, + 0X87, 0X66, 0X65, 0X54, 0X44, 0X33, 0X22, 0XFF, 0XFE, 0XEE, 0XDD, 0XDC, + 0XCC, 0XBB, 0XBA, 0X33, 0X22, 0X22, 0XFF, 0XEE, 0XED, 0XDD, 0XDC, 0XCB, + 0XBB, 0XBA, 0XAA, 0X99, 0X88, 0X87, 0X76, 0X66, 0X55, 0X54, 0X43, 0X32, + 0X22, 0XFE, 0XED, 0XCC, 0XBA, 0X98, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X0A, 0X96, + 0X54, 0X32, 0XFE, 0XDD, 0XCB, 0XBA, 0X99, 0X98, 0X87, 0X76, 0X65, 0X54, + 0X44, 0X33, 0X22, 0XFF, 0XFE, 0XEE, 0XDD, 0XDC, 0XCB, 0XBB, 0XBA, 0XAA, + 0X92, 0X2F, 0XFE, 0XEE, 0XED, 0XDD, 0XCC, 0XCB, 0XBB, 0XAA, 0XA9, 0X99, + 0X88, 0X87, 0X76, 0X66, 0X55, 0X54, 0X43, 0X32, 0X2F, 0XFE, 0XDD, 0XCB, + 0XA9, 0X98, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0XA9, 0X64, 0X32, 0XFE, 0XDD, + 0XCB, 0XBA, 0X99, 0X98, 0X87, 0X76, 0X65, 0X54, 0X43, 0X33, 0X22, 0XFF, + 0XFE, 0XEE, 0XDD, 0XDC, 0XCB, 0XBB, 0XAA, 0XA9, 0X99, 0X98, 0X87, 0XEE, + 0XDD, 0XDD, 0XCC, 0XBB, 0XBB, 0XAA, 0XA9, 0X98, 0X88, 0X77, 0X66, 0X65, + 0X55, 0X44, 0X33, 0X22, 0X2F, 0XEE, 0XDC, 0XBB, 0XA9, 0X81, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X32, 0XD5, 0X43, 0X2F, 0XED, 0XCB, 0XBA, 0XA9, 0X98, + 0X87, 0X76, 0X65, 0X54, 0X43, 0X33, 0X22, 0XFF, 0XFE, 0XED, 0XDD, 0XCC, + 0XCB, 0XBB, 0XAA, 0XA9, 0X99, 0X88, 0X87, 0X77, 0X6D, 0XDC, 0XCC, 0XBB, + 0XBA, 0XAA, 0X99, 0X98, 0X88, 0X77, 0X66, 0X65, 0X55, 0X44, 0X33, 0X22, + 0XFF, 0XED, 0XDC, 0XBA, 0X98, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, + 0X2D, 0XBA, 0X9F, 0XED, 0XDC, 0XBB, 0XA9, 0X98, 0X87, 0X76, 0X65, 0X54, + 0X43, 0X33, 0X22, 0XFF, 0XFE, 0XED, 0XDD, 0XCC, 0XBB, 0XBA, 0XAA, 0X99, + 0X99, 0X88, 0X77, 0X76, 0X66, 0X6C, 0XCB, 0XBB, 0XBA, 0XAA, 0X99, 0X88, + 0X87, 0X76, 0X66, 0X55, 0X54, 0X43, 0X32, 0X2F, 0XFE, 0XED, 0XCB, 0XBA, + 0X98, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X32, 0XDB, 0XA9, 0X7D, + 0XDC, 0XBB, 0XA9, 0X98, 0X87, 0X76, 0X65, 0X54, 0X43, 0X33, 0X22, 0XFF, + 0XEE, 0XED, 0XDD, 0XCC, 0XBB, 0XBA, 0XAA, 0X99, 0X98, 0X88, 0X77, 0X76, + 0X66, 0X55, 0X54, 0XBB, 0XAA, 0XA9, 0X99, 0X88, 0X87, 0X76, 0X66, 0X55, + 0X54, 0X43, 0X32, 0X2F, 0XFE, 0XDC, 0XCB, 0XA9, 0X81, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X03, 0X2D, 0XBA, 0X87, 0X65, 0X44, 0XA9, 0X98, + 0X87, 0X76, 0X65, 0X54, 0X43, 0X33, 0X22, 0XFF, 0XEE, 0XED, 0XDC, 0XCC, + 0XBB, 0XBA, 0XA9, 0X99, 0X98, 0X87, 0X77, 0X66, 0X66, 0X55, 0X44, 0X44, + 0X33, 0X39, 0X98, 0X88, 0X77, 0X66, 0X65, 0X55, 0X44, 0X33, 0X22, 0XFF, + 0XEE, 0XDC, 0XBA, 0X99, 0X81, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X3F, 0XCB, 0X98, 0X76, 0X54, 0X42, 0X28, 0X87, 0X76, 0X65, 0X54, + 0X43, 0X32, 0X22, 0XFF, 0XEE, 0XDD, 0XDC, 0XCB, 0XBB, 0XAA, 0XA9, 0X99, + 0X88, 0X87, 0X77, 0X66, 0X65, 0X55, 0X44, 0X43, 0X33, 0X22, 0X28, 0X88, + 0X77, 0X66, 0X65, 0X55, 0X44, 0X32, 0X2F, 0XFF, 0XED, 0XCC, 0XBA, 0X98, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFC, 0XA9, + 0X76, 0X54, 0X43, 0X22, 0XF7, 0X76, 0X65, 0X54, 0X43, 0X32, 0X2F, 0XFF, + 0XEE, 0XDD, 0XDC, 0XCB, 0XBB, 0XAA, 0X99, 0X99, 0X88, 0X77, 0X76, 0X66, + 0X55, 0X54, 0X44, 0X33, 0X32, 0X22, 0XFF, 0XFE, 0XED, 0X66, 0X55, 0X54, + 0X43, 0X32, 0X2F, 0XFE, 0XDC, 0XCB, 0XA9, 0X98, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XCA, 0X97, 0X65, 0X44, 0X22, + 0XFE, 0XED, 0XD5, 0X54, 0X43, 0X32, 0X2F, 0XFF, 0XEE, 0XDD, 0XCC, 0XCB, + 0XBA, 0XAA, 0X99, 0X98, 0X88, 0X77, 0X76, 0X66, 0X55, 0X44, 0X44, 0X33, + 0X22, 0X2F, 0XFF, 0XEE, 0XDD, 0XDD, 0X55, 0X54, 0X43, 0X22, 0XFF, 0XFE, + 0XDC, 0XBA, 0X99, 0X81, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X03, 0XFC, 0XA8, 0X76, 0X54, 0X32, 0X2F, 0XED, 0XDC, 0XCB, + 0XB3, 0X32, 0X2F, 0XFE, 0XEE, 0XDD, 0XCC, 0XBB, 0XBA, 0XA9, 0X99, 0X98, + 0X87, 0X77, 0X66, 0X65, 0X55, 0X44, 0X43, 0X33, 0X22, 0XFF, 0XFE, 0XED, + 0XDD, 0XDC, 0XCC, 0XBB, 0X33, 0X22, 0XFF, 0XED, 0XCC, 0XBA, 0X98, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0X2D, + 0XCA, 0X87, 0X54, 0X42, 0X2F, 0XEE, 0XDD, 0XCB, 0XBA, 0XA2, 0X2F, 0XFE, + 0XED, 0XDD, 0XCC, 0XBB, 0XBA, 0XA9, 0X99, 0X88, 0X87, 0X76, 0X66, 0X55, + 0X54, 0X44, 0X33, 0X32, 0X22, 0XFF, 0XEE, 0XDD, 0XDD, 0XCC, 0XCB, 0XBA, + 0XA9, 0X9F, 0XFF, 0XED, 0XCB, 0XA9, 0X98, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X32, 0XDB, 0X98, 0X75, 0X43, + 0X22, 0XFE, 0XDD, 0XCC, 0XBA, 0XA9, 0X98, 0X8E, 0XED, 0XDC, 0XCC, 0XBB, + 0XAA, 0X99, 0X99, 0X88, 0X77, 0X76, 0X66, 0X55, 0X44, 0X43, 0X33, 0X22, + 0X2F, 0XFE, 0XED, 0XDD, 0XDC, 0XCC, 0XBB, 0XAA, 0X99, 0X88, 0X87, 0X6C, + 0XCB, 0XA9, 0X81, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X03, 0X2D, 0XB9, 0X76, 0X54, 0X32, 0XFE, 0XED, 0XCC, + 0XBB, 0XA9, 0X98, 0X87, 0X76, 0X6C, 0XCB, 0XBB, 0XAA, 0X99, 0X98, 0X88, + 0X77, 0X66, 0X65, 0X54, 0X44, 0X43, 0X32, 0X22, 0XFF, 0XEE, 0XDD, 0XDD, + 0XCC, 0XCB, 0XBA, 0XA9, 0X98, 0X88, 0X76, 0X55, 0X43, 0X99, 0X81, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X32, 0XDB, 0X87, 0X64, 0X42, 0X2F, 0XED, 0XDC, 0XBB, 0XA9, 0X98, 0X87, + 0X76, 0X65, 0X5B, 0XBA, 0XA9, 0X99, 0X98, 0X87, 0X77, 0X66, 0X65, 0X54, + 0X44, 0X33, 0X22, 0X2F, 0XFE, 0XED, 0XDD, 0XDC, 0XCB, 0XBA, 0XAA, 0X98, + 0X88, 0X87, 0X65, 0X54, 0X32, 0X2F, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X03, 0XFC, 0XA8, 0X75, + 0X43, 0X2F, 0XEE, 0XDC, 0XBB, 0XA9, 0X98, 0X87, 0X76, 0X65, 0X54, 0X43, + 0X32, 0X99, 0X88, 0X87, 0X76, 0X66, 0X55, 0X44, 0X43, 0X32, 0X22, 0XFF, + 0XFE, 0XED, 0XDD, 0XCC, 0XBB, 0XAA, 0X99, 0X88, 0X87, 0X65, 0X54, 0X43, + 0X22, 0XF1, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X3F, 0XCA, 0X87, 0X44, 0X32, 0XFE, 0XDC, + 0XCB, 0XA9, 0X98, 0X87, 0X66, 0X55, 0X44, 0X43, 0X22, 0X22, 0X88, 0X77, + 0X76, 0X65, 0X54, 0X44, 0X33, 0X32, 0X2F, 0XFF, 0XEE, 0XDD, 0XDC, 0XCB, + 0XBA, 0XA9, 0X98, 0X88, 0X76, 0X55, 0X43, 0X22, 0XF1, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X03, 0XFC, 0X97, 0X64, 0X32, 0XFE, 0XDD, 0XCB, 0XAA, 0X98, 0X87, + 0X66, 0X55, 0X44, 0X33, 0X22, 0X2F, 0XFF, 0XEE, 0X66, 0X65, 0X54, 0X44, + 0X33, 0X22, 0X2F, 0XFE, 0XED, 0XDD, 0XCC, 0XBB, 0XAA, 0X99, 0X88, 0X87, + 0X65, 0X44, 0X32, 0X2F, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XAA, 0X78, + 0X75, 0X43, 0X2F, 0XED, 0XCB, 0XAA, 0X98, 0X77, 0X66, 0X55, 0X44, 0X32, + 0X22, 0XFF, 0XFE, 0XED, 0XDD, 0XCC, 0X44, 0X43, 0X32, 0X22, 0XFF, 0XEE, + 0XDD, 0XDC, 0XCB, 0XBA, 0XA9, 0X88, 0X88, 0X76, 0X54, 0X43, 0X2F, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XAA, 0X7E, 0X43, 0X2F, 0XED, + 0XCB, 0XBA, 0X98, 0X77, 0X66, 0X54, 0X43, 0X32, 0X22, 0XFF, 0XEE, 0XDD, + 0XDC, 0XCB, 0XBB, 0XAA, 0X22, 0X2F, 0XFE, 0XED, 0XDD, 0XCC, 0XBB, 0XAA, + 0X98, 0X88, 0X76, 0X55, 0X43, 0X22, 0XF1, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0XAA, 0X7E, 0XA9, 0XFE, 0XDC, 0XBA, 0X98, 0X77, + 0X66, 0X54, 0X43, 0X22, 0X2F, 0XFE, 0XED, 0XDD, 0XCC, 0XBB, 0XBA, 0XA9, + 0X99, 0XFF, 0XEE, 0XDD, 0XDC, 0XBB, 0XAA, 0XA9, 0X88, 0X87, 0X65, 0X44, + 0X32, 0XF1, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0XAA, 0X7E, 0XA9, 0X6C, 0XBA, 0X98, 0X76, 0X66, 0X54, 0X33, 0X22, + 0XFF, 0XEE, 0XDD, 0XDC, 0XBB, 0XBA, 0XA9, 0X99, 0X98, 0X87, 0X7D, 0XDD, + 0XCB, 0XBA, 0XA9, 0X98, 0X88, 0X76, 0X54, 0X43, 0XF1, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XAA, 0X7E, + 0XA7, 0X53, 0X98, 0X76, 0X65, 0X54, 0X32, 0X2F, 0XFE, 0XED, 0XDD, 0XCB, + 0XBB, 0XAA, 0X99, 0X98, 0X88, 0X77, 0X66, 0X65, 0X44, 0XAA, 0X98, 0X88, + 0X87, 0X64, 0X43, 0X2F, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XAA, 0X7B, 0X96, 0X3F, 0XE6, + 0X65, 0X43, 0X32, 0X2F, 0XEE, 0XDD, 0XCC, 0XBB, 0XBA, 0X99, 0X98, 0X88, + 0X77, 0X66, 0X65, 0X44, 0X33, 0X32, 0X88, 0X87, 0X65, 0X44, 0X3F, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0XAA, 0X7B, 0X93, 0XFE, 0XDC, 0XB3, 0X22, 0XFE, + 0XED, 0XDC, 0XCB, 0XBA, 0XA9, 0X98, 0X88, 0X77, 0X66, 0X65, 0X44, 0X33, + 0X32, 0XFF, 0XFF, 0XED, 0X44, 0X43, 0XF1, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0XAA, 0X7B, 0X5F, 0XDC, 0XAA, 0X98, 0XEE, 0XDD, 0XCB, 0XBB, 0XA9, + 0X98, 0X88, 0X77, 0X66, 0X54, 0X43, 0X33, 0X2F, 0XFF, 0XFF, 0XED, 0XBB, + 0XBA, 0X81, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XAA, 0X7B, + 0XEB, 0XA9, 0X77, 0X76, 0X65, 0XBB, 0XBA, 0X98, 0X88, 0X77, 0X65, 0X44, + 0X43, 0X32, 0XFF, 0XFF, 0XED, 0XDB, 0XBB, 0XBA, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XAA, 0XB9, 0X77, 0X64, 0X44, + 0X44, 0X43, 0X28, 0X88, 0X77, 0X65, 0X44, 0X43, 0X32, 0XFF, 0XED, 0XDC, + 0XBB, 0XBA, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0XAB, 0XBF, 0XFF, 0XFF, 0XFF, 0XEC, + 0X54, 0X44, 0X33, 0X2F, 0XED, 0XDD, 0XBB, 0XBA, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X66, 0XAB, 0XBB, 0XBA, 0X97, 0XED, 0XDD, + 0XDB, 0XBA, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, 0X11, + 0X11, 0X11, 0X11, 0X10, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X66, 0X66, 0X53, 0XBA, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X01, 0X11, 0X11, 0X11, 0X11, 0X11, 0X10, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, + 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00}; diff --git a/lib/Adafruit_ILI9341/examples/pyportal_boing/pyportal_boing.ino b/lib/Adafruit_ILI9341/examples/pyportal_boing/pyportal_boing.ino new file mode 100644 index 000000000..4c7689589 --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/pyportal_boing/pyportal_boing.ino @@ -0,0 +1,162 @@ +// 'Boing' ball demo for PyPortal. +// Requires Adafruit_GFX 1.4.5 or later and Adafruit_DMA + +#include "Adafruit_GFX.h" +#include "Adafruit_ILI9341.h" +#define SCREENWIDTH ILI9341_TFTHEIGHT // Native display orientation is +#define SCREENHEIGHT ILI9341_TFTWIDTH // vertical, so swap width/height +#include "graphics.h" + +#define TFT_D0 34 // Data bit 0 pin (MUST be on PORT byte boundary) +#define TFT_WR 26 // Write-strobe pin (CCL-inverted timer output) +#define TFT_DC 10 // Data/command pin +#define TFT_CS 11 // Chip-select pin +#define TFT_RST 24 // Reset pin +#define TFT_RD 9 // Read-strobe pin +#define TFT_BACKLIGHT 25 + +// ILI9341 with 8-bit parallel interface: +Adafruit_ILI9341 tft(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST, TFT_RD); + +#define BGCOLOR 0xAD75 +#define GRIDCOLOR 0xA815 +#define BGSHADOW 0x5285 +#define GRIDSHADOW 0x600C +#define RED 0xF800 +#define WHITE 0xFFFF + +#define YBOTTOM 123 // Ball Y coord at bottom + #define YBOUNCE -3.5 // Upward velocity on ball bounce + +// Ball coordinates are stored floating-point because screen refresh +// is so quick, whole-pixel movements are just too fast! +float ballx = 20.0, bally = YBOTTOM, // Current ball position + ballvx = 0.8, ballvy = YBOUNCE, // Ball velocity + ballframe = 3; // Ball animation frame # +int balloldx = ballx, balloldy = bally; // Prior ball position + +// Working buffer for ball rendering...2 scanlines that alternate, +// one is rendered while the other is transferred via DMA. +uint16_t renderbuf[2][SCREENWIDTH]; + +uint16_t palette[16]; // Color table for ball rotation effect + +uint32_t startTime, frame = 0; // For frames-per-second estimate + +void setup() { + Serial.begin(9600); +// while(!Serial); + + // Turn on backlight (required on PyPortal) + pinMode(TFT_BACKLIGHT, OUTPUT); + digitalWrite(TFT_BACKLIGHT, HIGH); + + tft.begin(); + tft.setRotation(3); // Landscape orientation, USB at bottom right + + // Draw initial framebuffer contents: + tft.drawBitmap(0, 0, (uint8_t *)background, + SCREENWIDTH, SCREENHEIGHT, GRIDCOLOR, BGCOLOR); + + startTime = millis(); +} + +void loop() { + + balloldx = (int16_t)ballx; // Save prior position + balloldy = (int16_t)bally; + ballx += ballvx; // Update position + bally += ballvy; + ballvy += 0.06; // Update Y velocity + if((ballx <= 15) || (ballx >= SCREENWIDTH - BALLWIDTH)) + ballvx *= -1; // Left/right bounce + if(bally >= YBOTTOM) { // Hit ground? + bally = YBOTTOM; // Clip and + ballvy = YBOUNCE; // bounce up + } + + // Determine screen area to update. This is the bounds of the ball's + // prior and current positions, so the old ball is fully erased and new + // ball is fully drawn. + int16_t minx, miny, maxx, maxy, width, height; + // Determine bounds of prior and new positions + minx = ballx; + if(balloldx < minx) minx = balloldx; + miny = bally; + if(balloldy < miny) miny = balloldy; + maxx = ballx + BALLWIDTH - 1; + if((balloldx + BALLWIDTH - 1) > maxx) maxx = balloldx + BALLWIDTH - 1; + maxy = bally + BALLHEIGHT - 1; + if((balloldy + BALLHEIGHT - 1) > maxy) maxy = balloldy + BALLHEIGHT - 1; + + width = maxx - minx + 1; + height = maxy - miny + 1; + + // Ball animation frame # is incremented opposite the ball's X velocity + ballframe -= ballvx * 0.5; + if(ballframe < 0) ballframe += 14; // Constrain from 0 to 13 + else if(ballframe >= 14) ballframe -= 14; + + // Set 7 palette entries to white, 7 to red, based on frame number. + // This makes the ball spin + for(uint8_t i=0; i<14; i++) { + palette[i+2] = ((((int)ballframe + i) % 14) < 7) ? WHITE : RED; + // Palette entries 0 and 1 aren't used (clear and shadow, respectively) + } + + // Only the changed rectangle is drawn into the 'renderbuf' array... + uint16_t c, *destPtr; + int16_t bx = minx - (int)ballx, // X relative to ball bitmap (can be negative) + by = miny - (int)bally, // Y relative to ball bitmap (can be negative) + bgx = minx, // X relative to background bitmap (>= 0) + bgy = miny, // Y relative to background bitmap (>= 0) + x, y, bx1, bgx1; // Loop counters and working vars + uint8_t p; // 'packed' value of 2 ball pixels + int8_t bufIdx = 0; + + tft.dmaWait(); // Wait for last line from prior call to complete + tft.endWrite(); + + tft.startWrite(); + tft.setAddrWindow(minx, miny, width, height); + + for(y=0; y= 0) && (bx1 < BALLWIDTH) && // Is current pixel row/column + (by >= 0) && (by < BALLHEIGHT)) { // inside the ball bitmap area? + // Yes, do ball compositing math... + p = ball[by][bx1 / 2]; // Get packed value (2 pixels) + c = (bx1 & 1) ? (p & 0xF) : (p >> 4); // Unpack high or low nybble + if(c == 0) { // Outside ball - just draw grid + c = background[bgy][bgx1 / 8] & (0x80 >> (bgx1 & 7)) ? GRIDCOLOR : BGCOLOR; + } else if(c > 1) { // In ball area... + c = palette[c]; + } else { // In shadow area... + c = background[bgy][bgx1 / 8] & (0x80 >> (bgx1 & 7)) ? GRIDSHADOW : BGSHADOW; + } + } else { // Outside ball bitmap, just draw background bitmap... + c = background[bgy][bgx1 / 8] & (0x80 >> (bgx1 & 7)) ? GRIDCOLOR : BGCOLOR; + } + *destPtr++ = c; // Store pixel color + bx1++; // Increment bitmap position counters (X axis) + bgx1++; + } + tft.dmaWait(); // Wait for prior line to complete + tft.writePixels(&renderbuf[bufIdx][0], width, false); // Non-blocking write + bufIdx = 1 - bufIdx; + by++; // Increment bitmap position counters (Y axis) + bgy++; + } + + // Show approximate frame rate + if(!(++frame & 255)) { // Every 256 frames... + uint32_t elapsed = (millis() - startTime) / 1000; // Seconds + if(elapsed) { + Serial.print(frame / elapsed); + Serial.println(" fps"); + } + } +} diff --git a/lib/Adafruit_ILI9341/examples/touchpaint/touchpaint.ino b/lib/Adafruit_ILI9341/examples/touchpaint/touchpaint.ino new file mode 100644 index 000000000..227abdc84 --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/touchpaint/touchpaint.ino @@ -0,0 +1,146 @@ +/*************************************************** + This is our touchscreen painting example for the Adafruit ILI9341 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 // Core graphics library +#include +#include // this is needed even tho we aren't using it +#include +#include + +// This is calibration data for the raw touch data to the screen coordinates +#define TS_MINX 150 +#define TS_MINY 130 +#define TS_MAXX 3800 +#define TS_MAXY 4000 + +// The STMPE610 uses hardware SPI on the shield, and #8 +#define STMPE_CS 8 +Adafruit_STMPE610 ts = Adafruit_STMPE610(STMPE_CS); + +// The display also uses hardware SPI, plus #9 & #10 +#define TFT_CS 10 +#define TFT_DC 9 +Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); + +// Size of the color selection boxes and the paintbrush size +#define BOXSIZE 40 +#define PENRADIUS 3 +int oldcolor, currentcolor; + +void setup(void) { + // while (!Serial); // used for leonardo debugging + + Serial.begin(9600); + Serial.println(F("Touch Paint!")); + + tft.begin(); + + if (!ts.begin()) { + Serial.println("Couldn't start touchscreen controller"); + while (1); + } + Serial.println("Touchscreen started"); + + tft.fillScreen(ILI9341_BLACK); + + // make the color selection boxes + tft.fillRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_RED); + tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, ILI9341_YELLOW); + tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, ILI9341_GREEN); + tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, ILI9341_CYAN); + tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, ILI9341_BLUE); + tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, ILI9341_MAGENTA); + + // select the current color 'red' + tft.drawRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + currentcolor = ILI9341_RED; +} + + +void loop() +{ + // See if there's any touch data for us + if (ts.bufferEmpty()) { + return; + } + /* + // You can also wait for a touch + if (! ts.touched()) { + return; + } + */ + + // Retrieve a point + TS_Point p = ts.getPoint(); + + /* + Serial.print("X = "); Serial.print(p.x); + Serial.print("\tY = "); Serial.print(p.y); + Serial.print("\tPressure = "); Serial.println(p.z); + */ + + // Scale from ~0->4000 to tft.width using the calibration #'s + p.x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width()); + p.y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height()); + + /* + Serial.print("("); Serial.print(p.x); + Serial.print(", "); Serial.print(p.y); + Serial.println(")"); + */ + + if (p.y < BOXSIZE) { + oldcolor = currentcolor; + + if (p.x < BOXSIZE) { + currentcolor = ILI9341_RED; + tft.drawRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*2) { + currentcolor = ILI9341_YELLOW; + tft.drawRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*3) { + currentcolor = ILI9341_GREEN; + tft.drawRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*4) { + currentcolor = ILI9341_CYAN; + tft.drawRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*5) { + currentcolor = ILI9341_BLUE; + tft.drawRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*6) { + currentcolor = ILI9341_MAGENTA; + tft.drawRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } + + if (oldcolor != currentcolor) { + if (oldcolor == ILI9341_RED) + tft.fillRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_RED); + if (oldcolor == ILI9341_YELLOW) + tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, ILI9341_YELLOW); + if (oldcolor == ILI9341_GREEN) + tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, ILI9341_GREEN); + if (oldcolor == ILI9341_CYAN) + tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, ILI9341_CYAN); + if (oldcolor == ILI9341_BLUE) + tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, ILI9341_BLUE); + if (oldcolor == ILI9341_MAGENTA) + tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, ILI9341_MAGENTA); + } + } + if (((p.y-PENRADIUS) > BOXSIZE) && ((p.y+PENRADIUS) < tft.height())) { + tft.fillCircle(p.x, p.y, PENRADIUS, currentcolor); + } +} diff --git a/lib/Adafruit_ILI9341/examples/touchpaint_featherwing/touchpaint_featherwing.ino b/lib/Adafruit_ILI9341/examples/touchpaint_featherwing/touchpaint_featherwing.ino new file mode 100644 index 000000000..e641f090b --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/touchpaint_featherwing/touchpaint_featherwing.ino @@ -0,0 +1,158 @@ +/*************************************************** + This is our touchscreen painting example for the Adafruit TFT FeatherWing + ----> http://www.adafruit.com/products/3315 + + Check out the links above for our tutorials and wiring diagrams + + 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 // this is needed even tho we aren't using it + +#include // Core graphics library +#include // Hardware-specific library +#include + +#ifdef ESP8266 + #define STMPE_CS 16 + #define TFT_CS 0 + #define TFT_DC 15 + #define SD_CS 2 +#elif defined(ESP32) && !defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) + #define STMPE_CS 32 + #define TFT_CS 15 + #define TFT_DC 33 + #define SD_CS 14 +#elif defined(TEENSYDUINO) + #define TFT_DC 10 + #define TFT_CS 4 + #define STMPE_CS 3 + #define SD_CS 8 +#elif defined(ARDUINO_STM32_FEATHER) + #define TFT_DC PB4 + #define TFT_CS PA15 + #define STMPE_CS PC7 + #define SD_CS PC5 +#elif defined(ARDUINO_NRF52832_FEATHER) /* BSP 0.6.5 and higher! */ + #define TFT_DC 11 + #define TFT_CS 31 + #define STMPE_CS 30 + #define SD_CS 27 +#elif defined(ARDUINO_MAX32620FTHR) || defined(ARDUINO_MAX32630FTHR) + #define TFT_DC P5_4 + #define TFT_CS P5_3 + #define STMPE_CS P3_3 + #define SD_CS P3_2 +#else + // Anything else, defaults! + #define STMPE_CS 6 + #define TFT_CS 9 + #define TFT_DC 10 + #define SD_CS 5 +#endif + + +Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); +Adafruit_STMPE610 ts = Adafruit_STMPE610(STMPE_CS); + + +// This is calibration data for the raw touch data to the screen coordinates +#define TS_MINX 3800 +#define TS_MAXX 100 +#define TS_MINY 100 +#define TS_MAXY 3750 + +// Size of the color selection boxes and the paintbrush size +#define BOXSIZE 40 +#define PENRADIUS 3 +int oldcolor, currentcolor; + +void setup(void) { + Serial.begin(115200); + + delay(10); + Serial.println("FeatherWing TFT"); + if (!ts.begin()) { + Serial.println("Couldn't start touchscreen controller"); + while (1); + } + Serial.println("Touchscreen started"); + + tft.begin(); + tft.fillScreen(ILI9341_BLACK); + + // make the color selection boxes + tft.fillRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_RED); + tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, ILI9341_YELLOW); + tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, ILI9341_GREEN); + tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, ILI9341_CYAN); + tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, ILI9341_BLUE); + tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, ILI9341_MAGENTA); + + // select the current color 'red' + tft.drawRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + currentcolor = ILI9341_RED; +} + +void loop() { + // Retrieve a point + TS_Point p = ts.getPoint(); + + Serial.print("X = "); Serial.print(p.x); + Serial.print("\tY = "); Serial.print(p.y); + Serial.print("\tPressure = "); Serial.println(p.z); + + + // Scale from ~0->4000 to tft.width using the calibration #'s + p.x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width()); + p.y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height()); + + if (p.y < BOXSIZE) { + oldcolor = currentcolor; + + if (p.x < BOXSIZE) { + currentcolor = ILI9341_RED; + tft.drawRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*2) { + currentcolor = ILI9341_YELLOW; + tft.drawRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*3) { + currentcolor = ILI9341_GREEN; + tft.drawRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*4) { + currentcolor = ILI9341_CYAN; + tft.drawRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*5) { + currentcolor = ILI9341_BLUE; + tft.drawRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*6) { + currentcolor = ILI9341_MAGENTA; + tft.drawRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } + + if (oldcolor != currentcolor) { + if (oldcolor == ILI9341_RED) + tft.fillRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_RED); + if (oldcolor == ILI9341_YELLOW) + tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, ILI9341_YELLOW); + if (oldcolor == ILI9341_GREEN) + tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, ILI9341_GREEN); + if (oldcolor == ILI9341_CYAN) + tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, ILI9341_CYAN); + if (oldcolor == ILI9341_BLUE) + tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, ILI9341_BLUE); + if (oldcolor == ILI9341_MAGENTA) + tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, ILI9341_MAGENTA); + } + } + + if (((p.y-PENRADIUS) > 0) && ((p.y+PENRADIUS) < tft.height())) { + tft.fillCircle(p.x, p.y, PENRADIUS, currentcolor); + } +} diff --git a/lib/Adafruit_ILI9341/examples/touchpaint_pyportal/.pyportal.test.only b/lib/Adafruit_ILI9341/examples/touchpaint_pyportal/.pyportal.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/lib/Adafruit_ILI9341/examples/touchpaint_pyportal/touchpaint_pyportal.ino b/lib/Adafruit_ILI9341/examples/touchpaint_pyportal/touchpaint_pyportal.ino new file mode 100644 index 000000000..d25f77fe0 --- /dev/null +++ b/lib/Adafruit_ILI9341/examples/touchpaint_pyportal/touchpaint_pyportal.ino @@ -0,0 +1,114 @@ +#include "Adafruit_ILI9341.h" +#include "TouchScreen.h" + +#define YP A4 +#define XM A7 +#define YM A6 +#define XP A5 +TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); +#define X_MIN 325 +#define X_MAX 750 +#define Y_MIN 840 +#define Y_MAX 240 + +// Pin settings for PyPortal +#define TFT_D0 34 // Data bit 0 pin (MUST be on PORT byte boundary) +#define TFT_WR 26 // Write-strobe pin (CCL-inverted timer output) +#define TFT_DC 10 // Data/command pin +#define TFT_CS 11 // Chip-select pin +#define TFT_RST 24 // Reset pin +#define TFT_RD 9 // Read-strobe pin +#define TFT_BACKLIGHT 25 +// ILI9341 with 8-bit parallel interface: +Adafruit_ILI9341 tft = Adafruit_ILI9341(tft8bitbus, TFT_D0, TFT_WR, TFT_DC, TFT_CS, TFT_RST, TFT_RD); + +// Size of the color selection boxes and the paintbrush size +#define BOXSIZE 40 +#define PENRADIUS 3 +int oldcolor, currentcolor; + +void setup(void) { + // while (!Serial); // used for leonardo debugging + + delay(100); + Serial.begin(9600); + Serial.println(F("Touch Paint!")); + + tft.begin(); + + tft.fillScreen(ILI9341_BLACK); + + // make the color selection boxes + tft.fillRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_RED); + tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, ILI9341_YELLOW); + tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, ILI9341_GREEN); + tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, ILI9341_CYAN); + tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, ILI9341_BLUE); + tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, ILI9341_MAGENTA); + + // select the current color 'red' + tft.drawRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + currentcolor = ILI9341_RED; + + pinMode(TFT_BACKLIGHT, OUTPUT); + digitalWrite(TFT_BACKLIGHT, HIGH); + +} + + +void loop() +{ + TSPoint p = ts.getPoint(); + // we have some minimum pressure we consider 'valid' + // pressure of 0 means no pressing! + if ((p.z < 100) || (p.z > 1000)) { + return; + } + Serial.print("X = "); Serial.print(p.x); + Serial.print("\tY = "); Serial.print(p.y); + Serial.print("\tPressure = "); Serial.println(p.z); + p.x = map(p.x, X_MIN, X_MAX, 0, 240); + p.y = map(p.y, Y_MIN, Y_MAX, 0, 320); + + if (p.y < BOXSIZE) { + oldcolor = currentcolor; + + if (p.x < BOXSIZE) { + currentcolor = ILI9341_RED; + tft.drawRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*2) { + currentcolor = ILI9341_YELLOW; + tft.drawRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*3) { + currentcolor = ILI9341_GREEN; + tft.drawRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*4) { + currentcolor = ILI9341_CYAN; + tft.drawRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*5) { + currentcolor = ILI9341_BLUE; + tft.drawRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } else if (p.x < BOXSIZE*6) { + currentcolor = ILI9341_MAGENTA; + tft.drawRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, ILI9341_WHITE); + } + + if (oldcolor != currentcolor) { + if (oldcolor == ILI9341_RED) + tft.fillRect(0, 0, BOXSIZE, BOXSIZE, ILI9341_RED); + if (oldcolor == ILI9341_YELLOW) + tft.fillRect(BOXSIZE, 0, BOXSIZE, BOXSIZE, ILI9341_YELLOW); + if (oldcolor == ILI9341_GREEN) + tft.fillRect(BOXSIZE*2, 0, BOXSIZE, BOXSIZE, ILI9341_GREEN); + if (oldcolor == ILI9341_CYAN) + tft.fillRect(BOXSIZE*3, 0, BOXSIZE, BOXSIZE, ILI9341_CYAN); + if (oldcolor == ILI9341_BLUE) + tft.fillRect(BOXSIZE*4, 0, BOXSIZE, BOXSIZE, ILI9341_BLUE); + if (oldcolor == ILI9341_MAGENTA) + tft.fillRect(BOXSIZE*5, 0, BOXSIZE, BOXSIZE, ILI9341_MAGENTA); + } + } + if (((p.y-PENRADIUS) > BOXSIZE) && ((p.y+PENRADIUS) < tft.height())) { + tft.fillCircle(p.x, p.y, PENRADIUS, currentcolor); + } +} diff --git a/lib/Adafruit_ILI9341/library.properties b/lib/Adafruit_ILI9341/library.properties new file mode 100644 index 000000000..f8f048ee1 --- /dev/null +++ b/lib/Adafruit_ILI9341/library.properties @@ -0,0 +1,10 @@ +name=Adafruit ILI9341 ESPEasy +version=1.5.10 +author=Adafruit +maintainer=Adafruit +sentence=Library for Adafruit ILI9341 displays, with ESPEasy specific modifications (added device support for ILI9342/ILI948x) +paragraph=Library for Adafruit ILI9341 displays, with ESPEasy specific modifications (added device support for ILI9342/ILI948x) +category=Display +url=https://github.com/adafruit/Adafruit_ILI9341 +architectures=* +depends=Adafruit GFX Library, Adafruit STMPE610, Adafruit TouchScreen diff --git a/lib/IRremoteESP8266/.github/workflows/Build.yml b/lib/IRremoteESP8266/.github/workflows/Build.yml index 5c0e5907c..dd4504361 100644 --- a/lib/IRremoteESP8266/.github/workflows/Build.yml +++ b/lib/IRremoteESP8266/.github/workflows/Build.yml @@ -7,9 +7,31 @@ on: branches: [ master ] jobs: - Build_Examples: - + Gen_Matrix: + outputs: + matrix: ${{ steps.files.outputs.matrix }} runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Generate Matrix of all examples + id: files + run: | + JSONI=$(find . -name platformio.ini -type f | sed 's,/platformio.ini$,,' | xargs -n 1 -I {} echo -e '"{}",') + + # Remove last "," and add closing brackets + if [[ $JSONI == *, ]]; then + JSONI="${JSONI%?}" + fi + JSONI=${JSONI//$'\n'} + echo $JSONI + # Set output + echo "::set-output name=matrix::[${JSONI}]" + Build_Example: + needs: Gen_Matrix + runs-on: ubuntu-latest + strategy: + matrix: + project: ${{fromJson(needs.Gen_Matrix.outputs.matrix)}} steps: - uses: actions/checkout@v2 @@ -24,14 +46,29 @@ jobs: uses: actions/cache@v2 with: path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + key: ${{ runner.os }}-${{ hashFiles('**/platformio.ini') }} + - name: Cache PlatformIO build + uses: actions/cache@v2 + with: + path: .pio + key: pio-${{ runner.os }}-${{ matrix.project }} + restore-keys: | + pio-${{ runner.os }}- + pio- - name: Set up Python uses: actions/setup-python@v2 - name: Install PlatformIO run: | python -m pip install --upgrade pip pip install --upgrade platformio - - name: Build all the examples + - name: Build example env: PLATFORMIO_BUILD_CACHE_DIR: "../../.pio/buildcache" - run: find . -name platformio.ini -type f | sed 's,/platformio.ini$,,' | xargs --verbose -n 1 pio run --jobs 2 --project-dir + run: pio run --jobs 2 --project-dir ${{ matrix.project }} + + Build_Examples: + needs: Build_Example + runs-on: ubuntu-latest + steps: + - name: done + run: echo ok diff --git a/lib/IRremoteESP8266/.github/workflows/MetaChecks.yml b/lib/IRremoteESP8266/.github/workflows/MetaChecks.yml index 2866ffc37..d8dcd0703 100644 --- a/lib/IRremoteESP8266/.github/workflows/MetaChecks.yml +++ b/lib/IRremoteESP8266/.github/workflows/MetaChecks.yml @@ -37,7 +37,7 @@ jobs: - uses: actions/checkout@v2 - name: Check all the version numbers match. run: | - LIB_VERSION=$(egrep "^#define\s+_IRREMOTEESP8266_VERSION_\s+" src/IRremoteESP8266.h | cut -d\" -f2) + LIB_VERSION=$(tools/extract_lib_version.sh) test ${LIB_VERSION} == "$(jq -r .version library.json)" grep -q "^version=${LIB_VERSION}$" library.properties examples-have-platformio_ini: diff --git a/lib/IRremoteESP8266/.github/workflows/UnitTests.yml b/lib/IRremoteESP8266/.github/workflows/UnitTests.yml index bbc486b18..a99e18c80 100644 --- a/lib/IRremoteESP8266/.github/workflows/UnitTests.yml +++ b/lib/IRremoteESP8266/.github/workflows/UnitTests.yml @@ -3,21 +3,28 @@ name: Tests on: [push, pull_request] jobs: - Unit_Tests: - + Tools_Tests: runs-on: ubuntu-latest + env: + MAKEFLAGS: "-j 2" + steps: + - uses: actions/checkout@v2 + - name: Build tools unit tests + run: (cd tools; make all) + - name: Run tools unit tests + run: (cd tools; make run_tests) + Unit_Tests: + runs-on: ubuntu-latest + env: + MAKEFLAGS: "-j 2" steps: - uses: actions/checkout@v2 - name: Install the Google test suite - env: - MAKEFLAGS: "-j 2" run: (cd test; make install-googletest) - - name: Build and run the library unit tests. - env: - MAKEFLAGS: "-j 2" + - name: Build base unit test + run: (cd test; make IRac_test) + - name: Build library unit tests + run: (cd test; make all) + - name: Run library unit tests run: (cd test; make run) - - name: Build and run the tools unit tests. - env: - MAKEFLAGS: "-j 2" - run: (cd tools; make all; make run_tests) diff --git a/lib/IRremoteESP8266/.github/workflows/codeql-analysis.yml b/lib/IRremoteESP8266/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..48500339a --- /dev/null +++ b/lib/IRremoteESP8266/.github/workflows/codeql-analysis.yml @@ -0,0 +1,72 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '38 1 * * 3' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/lib/IRremoteESP8266/README.md b/lib/IRremoteESP8266/README.md index f392c5529..e9d16eddd 100644 --- a/lib/IRremoteESP8266/README.md +++ b/lib/IRremoteESP8266/README.md @@ -10,8 +10,8 @@ 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.8.1 Now Available -Version 2.8.1 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. +## v2.8.2 Now Available +Version 2.8.2 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/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. diff --git a/lib/IRremoteESP8266/README_de.md b/lib/IRremoteESP8266/README_de.md index 884628149..50dbab2cb 100644 --- a/lib/IRremoteESP8266/README_de.md +++ b/lib/IRremoteESP8266/README_de.md @@ -11,8 +11,8 @@ Diese Programmbibliothek ermöglicht das **Senden _und_ Empfangen** von Infrarotsignalen mit [ESP8266](https://github.com/esp8266/Arduino)- und [ESP32](https://github.com/espressif/arduino-esp32)-Mikrocontrollern mithilfe des [Arduino-Frameworks](https://www.arduino.cc/) und handelsüblichen 940nm Infrarot-LEDs undIR-Empfängermodulen, wie zum Beispiel TSOP{17,22,24,36,38,44,48}*-Demodulatoren. -## v2.8.1 jetzt verfügbar -Version 2.8.1 der Bibliothek ist nun [verfügbar](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Die [Versionshinweise](ReleaseNotes.md) enthalten alle wichtigen Neuerungen. +## v2.8.2 jetzt verfügbar +Version 2.8.2 der Bibliothek ist nun [verfügbar](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Die [Versionshinweise](ReleaseNotes.md) enthalten alle wichtigen Neuerungen. #### Hinweis für Nutzer von Versionen vor v2.0 Die Benutzung der Bibliothek hat sich mit Version 2.0 leicht geändert. Einige Anpassungen im aufrufenden Code werden nötig sein, um mit Version ab 2.0 korrekt zu funktionieren. Mehr zu den Anpassungen finden sich auf unserer [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0)-Seite. diff --git a/lib/IRremoteESP8266/README_fr.md b/lib/IRremoteESP8266/README_fr.md index 3f1a93048..71f4d4abc 100644 --- a/lib/IRremoteESP8266/README_fr.md +++ b/lib/IRremoteESP8266/README_fr.md @@ -10,8 +10,8 @@ Cette librairie vous permetra de **recevoir et d'envoyer des signaux** infrarouge sur le protocole [ESP8266](https://github.com/esp8266/Arduino) ou sur le protocole [ESP32](https://github.com/espressif/arduino-esp32) en utilisant le [Arduino framework](https://www.arduino.cc/) qui utilise la norme 940nm IR LEDs et le module basique de reception d'onde IR. Exemple : TSOP{17,22,24,36,38,44,48}* modules etc. -## v2.8.1 disponible -Version 2.8.1 de la libraire est maintenant [disponible](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Vous pouvez voir le [Release Notes](ReleaseNotes.md) pour tous les changements importants. +## v2.8.2 disponible +Version 2.8.2 de la libraire est maintenant [disponible](https://github.com/crankyoldgit/IRremoteESP8266/releases/latest). Vous pouvez voir le [Release Notes](ReleaseNotes.md) pour tous les changements importants. #### mise à jour depuis pre-v2.0 L'utilisation de la librairie à un peu changer depuis la version in v2.0. Si vous voulez l'utiliser vous devrez changer votre utilisation aussi. Vous pouvez vous renseigner sur les précondition d'utilisation ici : [Upgrade to v2.0](https://github.com/crankyoldgit/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. diff --git a/lib/IRremoteESP8266/ReleaseNotes.md b/lib/IRremoteESP8266/ReleaseNotes.md index 9c8d7df98..5fd04c726 100644 --- a/lib/IRremoteESP8266/ReleaseNotes.md +++ b/lib/IRremoteESP8266/ReleaseNotes.md @@ -1,5 +1,38 @@ # Release Notes +## _v2.8.2 (20220314)_ + +**[Bug Fixes]** +- ESP32-C3: Fix reboot/crashes on ESP32-C3s when receiving. (#1768 #1751) + +**[Features]** +- HITACHI_AC296: Add `IRac` class support & tests. (#1776 #1758 #1757) +- Support for Hitachi RAS-70YHA3 (remote RAR-3U3) (#1758 #1757) +- LG: Add Swing Toggle support for Model `LG6711A20083V` (#1771 #1770) +- IRMQTTServer: add `MQTT_SERVER_AUTODETECT_ENABLE` via mqtt mDNS (#1769) +- Experimental basic support for Kelon 168 bit / 21 byte protocol. (#1747 #1745 #1744) +- MitsubishiAC: Tweak repeat gap timing. (#1760 #1759) +- Gree YAP0F8 (Detected as Kelvinator) vertical position set support (#1756) +- Make KELON (48 bit) protocol decoding stricter. (#1746 #1744) +- IRMQTTServer V1.6.1 (#1740 #1739 #1729) +- HITACHI_AC264: Add minimal detailed support. (#1735 #1729) +- LG2: Improve Light toggle msg handling. (#1738 #1737) +- MIDEA: Add support for Quiet, Clean & Freeze Protect controls. (#1734 #1733) +- Add basic support for HITACHI_AC264 264bit protocol. (#1730 #1729) +- ESP32-C3: Work around for some C3 specific compiler issues again. (#1732 #1695) + +**[Misc]** +- MIDEA: Update supported devices (#1774 #1773 #1716) +- Update devices supported by ELECTRA_AC (#1766 #1765) +- Improve documentation for `encodePioneer()` (#1761 #1749) +- Update (un)supported DAIKIN128 devices. (#1752) +- Refactor `decodeCOOLIX()` code & add another test case. (#1750 #1748) +- Simplify code based on state_t being initialised by default. (#1736 #1699) +- Add comments to help Teknopoint users. (#1731 #1728) +- Fix library version string calculation. (#1727 #1725) +- Confirm we can reproduce `TurnOnFujitsuAC.ino` via IRac/IRMQTTServer. (#1726 #1701) + + ## _v2.8.1 (20220101)_ **[Bug Fixes]** diff --git a/lib/IRremoteESP8266/SupportedProtocols.md b/lib/IRremoteESP8266/SupportedProtocols.md index 0b6fb06c1..509e7cd50 100644 --- a/lib/IRremoteESP8266/SupportedProtocols.md +++ b/lib/IRremoteESP8266/SupportedProtocols.md @@ -1,6 +1,6 @@ + Last generated: Mon 14 Mar 2022 06:52:21 +0000 ---> # IR Protocols supported by this library | Protocol | Brand | Model | A/C Model | Detailed A/C Support | @@ -22,12 +22,13 @@ | [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Tokio](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | AATOEMF17-12CHR1SW split-type RG51\|50/BGE Remote | | Yes | | [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | RAS-4M27YAV-E A/C
RAS-M10YKV-E A/C
RAS-M13YKV-E A/C
WH-E1YE remote | | Yes | | [Corona](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Corona.cpp) | **[Corona](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Corona.h)** | AR-01 remote
CSH-N2211 A/C
CSH-N2511 A/C
CSH-N2811 A/C
CSH-N4011 A/C | | 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 (DAIKIN160)
ARC433** remote (DAIKIN)
ARC433B69 remote (DAIKIN216)
ARC466A33 remote (DAIKIN)
ARC477A1 remote (DAIKIN2)
ARC480A5 remote (DAIKIN152)
ARC484A4 remote (DAIKIN216)
BRC4C151 remote (DAIKIN176)
BRC4C153 remote (DAIKIN176)
BRC52B63 remote (DAIKIN128)
DGS01 remote (DAIKIN64)
FFN-C/FCN-F Series A/C (DAIKIN64)
FFQ35B8V1B A/C (DAIKIN176)
FTE12HV2S A/C
FTQ60TV16U2 A/C (DAIKIN216)
FTWX35AXV1 A/C (DAIKIN64)
FTXB09AXVJU A/C (DAIKIN128)
FTXB12AXVJU A/C (DAIKIN128)
FTXM-M A/C (DAIKIN)
FTXZ25NV1B A/C (DAIKIN2)
FTXZ35NV1B A/C (DAIKIN2)
FTXZ50NV1B A/C (DAIKIN2)
M Series A/C (DAIKIN) | | 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 FTXB09AXVJU A/C (DAIKIN128)
17 Series FTXB12AXVJU A/C (DAIKIN128)
17 Series FTXB24AXVJU A/C (NOT SUPPORTED)
ARC423A5 remote (DAIKIN160)
ARC433** remote (DAIKIN)
ARC433B69 remote (DAIKIN216)
ARC466A33 remote (DAIKIN)
ARC477A1 remote (DAIKIN2)
ARC480A5 remote (DAIKIN152)
ARC484A4 remote (DAIKIN216)
BRC4C151 remote (DAIKIN176)
BRC4C153 remote (DAIKIN176)
BRC52B63 remote (DAIKIN128)
DGS01 remote (DAIKIN64)
FFN-C/FCN-F Series A/C (DAIKIN64)
FFQ35B8V1B A/C (DAIKIN176)
FTE12HV2S A/C
FTQ60TV16U2 A/C (DAIKIN216)
FTWX35AXV1 A/C (DAIKIN64)
FTXM-M A/C (DAIKIN)
FTXZ25NV1B A/C (DAIKIN2)
FTXZ35NV1B A/C (DAIKIN2)
FTXZ50NV1B A/C (DAIKIN2)
M Series A/C (DAIKIN) | | Yes | | [Delonghi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Delonghi.cpp) | **[Delonghi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Delonghi.h)** | PAC A95 | | Yes | | [Denon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Denon.cpp) | **Denon** | AVR-3801 A/V Receiver (probably) | | - | | [Dish](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Dish.cpp) | **DISH NETWORK** | echostar 301 | | - | | [Doshisha](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Doshisha.cpp) | **Doshisha** | CZ-S32D LED Light
CZ-S38D LED Light
CZ-S50D LED Light
RCZ01 remote | | - | | [Ecoclim](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Ecoclim.cpp) | **[EcoClim](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Ecoclim.h)** | HYSFR-P348 remote
ZC200DPO A/C | | Yes | +| [Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **[AEG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.h)** | Chillflex Pro AXP26U338CW A/C | | Yes | | [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 | | [Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **[Centek](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.h)** | SCT-65Q09 A/C
YKR-P/002E remote | | Yes | | [Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **[Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.h)** | Classic INV 17 / AXW12DCS A/C
YKR-M/003E remote | | Yes | @@ -52,15 +53,17 @@ | [Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.cpp) | **[Daichi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.h)** | D-H A/C (HAIER_AC176) | | 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 (HAIER_AC_YRW02)
HSU07-HEA03 remote (HAIER_AC)
V9014557 M47 8D remote (HAIER_AC176)
YR-W02 remote (HAIER_AC_YRW02) | | Yes | | [Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.cpp) | **[Mabe](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.h)** | MMI18HDBWCA6MI8 A/C (HAIER_AC176)
V12843 HJ200223 remote (HAIER_AC176) | | 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)** | KAZE-312KSDP A/C (HITACHI_AC1)
LT0541-HTA remote (HITACHI_AC1)
PC-LH3B (HITACHI_AC3)
R-LT0541-HTA/Y.K.1.1-1 V2.3 remote (HITACHI_AC1)
RAR-8P2 remote (HITACHI_AC424)
RAS-22NK A/C (HITACHI_AC344)
RAS-35THA6 remote
RAS-AJ25H A/C (HITACHI_AC424)
RF11T1 remote (HITACHI_AC344)
Series VI A/C (Circa 2007) (HITACHI_AC1) | | 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)** | KAZE-312KSDP A/C (HITACHI_AC1)
LT0541-HTA remote (HITACHI_AC1)
PC-LH3B (HITACHI_AC3)
R-LT0541-HTA/Y.K.1.1-1 V2.3 remote (HITACHI_AC1)
RAK-25NH5 A/C (HITACHI_AC264)
RAR-2P2 remote (HITACHI_AC264)
RAR-3U3 remote (HITACHI_AC296)
RAR-8P2 remote (HITACHI_AC424)
RAS-22NK A/C (HITACHI_AC344)
RAS-35THA6 remote
RAS-70YHA3 A/C (HITACHI_AC296)
RAS-AJ25H A/C (HITACHI_AC424)
RF11T1 remote (HITACHI_AC344)
Series VI A/C (Circa 2007) (HITACHI_AC1) | | 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) | **JVC** | PTU94023B remote | | - | -| [Kelon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.cpp) | **[Kelon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.h)** | ON/OFF 9000-12000 | | Yes | +| [Kelon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.cpp) | **[Hisense](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.h)** | AST-09UW4RVETG00A A/C (KELON168) | | Yes | +| [Kelon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.cpp) | **[Kelon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelon.h)** | AST-09UW4RVETG00A A/C (KELON168)
DG11R2-01 remote (KELON168)
ON/OFF 9000-12000 (KELON) | | Yes | +| [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | YAP0F8 remote | | Yes | | [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 | | [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Sharp](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | A5VEY A/C
YB1FA remote | | Yes | -| [LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[General Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711AR2853M A/C Remote (LG)
AG1BH09AW101 Split A/C (LG) | | 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 (LG)
A4UW30GFA2 A/C (LG2 - AKB74955603 & AKB73757604)
AKB73315611 remote (LG2 - AKB74955603)
AKB73757604 remote (LG2 - AKB73757604)
AKB74395308 remote (LG2)
AKB74955603 remote (LG2 - AKB74955603)
AKB75215403 remote (LG2)
AMNW09GSJA0 A/C (LG2 - AKB74955603)
AMNW24GTPA1 A/C (LG2 - AKB73757604)
MS05SQ NW0 A/C (LG2 - AKB74955603)
S4-W12JA3AA A/C (LG2) | | Yes | +| [LG](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.cpp) | **[General Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h)** | 6711AR2853M Remote (LG - GE6711AR2853M)
AG1BH09AW101 A/C (LG - GE6711AR2853M) | | 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 (LG - LG6711A20083V)
A4UW30GFA2 A/C (LG2 - AKB74955603 & AKB73757604)
AKB73315611 remote (LG2 - AKB74955603)
AKB73757604 remote (LG2 - AKB73757604)
AKB74395308 remote (LG2)
AKB74955603 remote (LG2 - AKB74955603)
AKB75215403 remote (LG2)
AMNW09GSJA0 A/C (LG2 - AKB74955603)
AMNW24GTPA1 A/C (LG2 - AKB73757604)
MS05SQ NW0 A/C (LG2 - AKB74955603)
S4-W12JA3AA A/C (LG2)
TS-H122ERM1 remote (LG - LG6711A20083V) | | Yes | | [Lasertag](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Lasertag.cpp) | **Lasertag** | Phaser emitters | | - | | [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) | **Lutron** | MIR-ITFS remote
MIR-ITFS-F remote
MIR-ITFS-LF remote
SP-HT remote | | - | @@ -71,6 +74,7 @@ | [Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[Danby](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.h)** | DAC080BGUWDB (MIDEA)
DAC100BGUWDB (MIDEA)
DAC120BGUWDB (MIDEA)
R09C/BCGE remote (MIDEA) | | Yes | | [Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[Kaysun](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.h)** | Casual CF A/C (MIDEA) | | 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 (MIDEA) | | Yes | +| [Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[Lennox](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.h)** | M22A indoor split A/C (MIDEA)
M33A indoor split A/C (MIDEA)
M33B indoor split A/C (MIDEA)
MCFA indoor split A/C (MIDEA)
MCFB indoor split A/C (MIDEA)
MMDA indoor split A/C (MIDEA)
MMDB indoor split A/C (MIDEA)
MWMA indoor split A/C (MIDEA)
MWMA009S4-3P A/C (MIDEA)
MWMA012S4-3P A/C (MIDEA)
MWMB indoor split A/C (MIDEA)
RG57A6/BGEFU1 remote (MIDEA) | | Yes | | [Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.h)** | FS40-7AR Stand Fan (MIDEA24) | | Yes | | [Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.cpp) | **[MrCool](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Midea.h)** | RG57A6/BGEFU1 remote (MIDEA) | | 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)** | RG66B6(B)/BGEFU1 remote (MIDEA)
RUBO18GMFILCAD A/C (18K BTU) (MIDEA)
RYBO12GMFILCAD A/C (12K BTU) (MIDEA)
UB018GMFILCFHD A/C (12K BTU) (MIDEA)
WS012GMFI22HLD A/C (12K BTU) (MIDEA)
WS018GMFI22HLD A/C (12K BTU) (MIDEA) | | Yes | @@ -80,7 +84,7 @@ | [Mirage](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.cpp) | **[Mirage](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.h)** | VLU series A/C | KKG29AC1
KKG9AC1 | Yes | | [Mirage](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.cpp) | **[Tronitechnik](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.h)** | KKG29A-C1 remote
Reykir 9000 A/C | KKG29AC1
KKG9AC1 | 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 (MITSUBISHI2)
KM14A 0179213 remote
MS-GK24VA A/C
TV (MITSUBISHI) | | Yes | -| [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | 001CP T7WE10714 remote (MITSUBISHI136)
KPOA remote (MITSUBISHI112)
MLZ-RX5017AS A/C (MITSUBISHI_AC)
MSH-A24WV A/C (MITSUBISHI112)
MSZ-FHnnVE A/C (MITSUBISHI_AC)
MSZ-GV2519 A/C (MITSUBISHI_AC)
MSZ-SF25VE3 A/C (MITSUBISHI_AC)
MSZ-ZW4017S A/C (MITSUBISHI_AC)
MUH-A24WV A/C (MITSUBISHI112)
PEAD-RP71JAA Ducted A/C (MITSUBISHI136)
RH151/M21ED6426 remote (MITSUBISHI_AC)
SG153/M21EDF426 remote (MITSUBISHI_AC)
SG15D remote (MITSUBISHI_AC) | | Yes | +| [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | 001CP T7WE10714 remote (MITSUBISHI136)
KPOA remote (MITSUBISHI112)
MLZ-RX5017AS A/C (MITSUBISHI_AC)
MSH-A24WV A/C (MITSUBISHI112)
MSZ-FHnnVE A/C (MITSUBISHI_AC)
MSZ-GV2519 A/C (MITSUBISHI_AC)
MSZ-SF25VE3 A/C (MITSUBISHI_AC)
MSZ-ZW4017S A/C (MITSUBISHI_AC)
MUH-A24WV A/C (MITSUBISHI112)
PEAD-RP71JAA Ducted A/C (MITSUBISHI136)
RH151 remote (MITSUBISHI_AC)
RH151/M21ED6426 remote (MITSUBISHI_AC)
SG153/M21EDF426 remote (MITSUBISHI_AC)
SG15D remote (MITSUBISHI_AC) | | 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 (88 bit)
RLA502A700B remote (152 bit)
SRKxxZJ-S A/C (88 bit)
SRKxxZM-S A/C (152 bit)
SRKxxZMXA-S A/C (152 bit) | | Yes | | [Multibrackets](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Multibrackets.cpp) | **Multibrackets** | Motorized Swing mount large - 4500 | | - | | [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Aloka](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | SleepyLights LED Lamp | | - | @@ -114,7 +118,7 @@ | [Tcl](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.cpp) | **[Teknopoint](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Tcl.h)** | Allegro SSA-09H A/C (GZ055BE1)
GZ-055B-E1 remote (GZ055BE1) | GZ055BE1
TAC09CHSD | Yes | | [Technibel](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Technibel.cpp) | **[Technibel](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Technibel.h)** | IRO PLUS | | 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 | -| [Teknopoint](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teknopoint.cpp) | **Teknopoint** | Allegro SSA-09H A/C
GZ-055B-E1 remote | | - | +| [Teknopoint](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Teknopoint.cpp) | **Teknopoint** | Allegro SSA-09H A/C
GZ-055B-E1 remote
GZ01-BEJ0-000 remote | | - | | [Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.cpp) | **[Carrier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Toshiba.h)** | 42NQV025M2 / 38NYV025M2 A/C
42NQV035M2 / 38NYV035M2 A/C
42NQV050M2 / 38NYV050M2 A/C
42NQV060M2 / 38NYV060M2 A/C | | 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-2558V A/C
RAS-25SKVP2-ND A/C
RAS-B13N3KV2
RAS-B13N3KVP-E
WC-L03SE
WH-TA01JE remote
WH-TA04NE
WH-UB03NJ remote | | Yes | | [Transcold](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Transcold.cpp) | **[Transcold](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Transcold.h)** | M1-F-NO-6 A/C | | Yes | @@ -179,12 +183,15 @@ - HITACHI_AC - HITACHI_AC1 - HITACHI_AC2 +- HITACHI_AC264 +- HITACHI_AC296 - HITACHI_AC3 - HITACHI_AC344 - HITACHI_AC424 - INAX - JVC - KELON +- KELON168 - KELVINATOR - LASERTAG - LEGOPF diff --git a/lib/IRremoteESP8266/assets/images/banner.svg b/lib/IRremoteESP8266/assets/images/banner.svg new file mode 100644 index 000000000..d1e2c3592 --- /dev/null +++ b/lib/IRremoteESP8266/assets/images/banner.svg @@ -0,0 +1,142 @@ +
Custom logo

IRremoteESP8266

Infrared remote library for ESP8266/ESP32: send and receive infrared signals with multiple protocols

+
\ No newline at end of file diff --git a/lib/IRremoteESP8266/assets/images/logo.svg b/lib/IRremoteESP8266/assets/images/logo.svg new file mode 100644 index 000000000..a42691f96 --- /dev/null +++ b/lib/IRremoteESP8266/assets/images/logo.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/IRremoteESP8266/examples/DumbIRRepeater/DumbIRRepeater.ino b/lib/IRremoteESP8266/examples/DumbIRRepeater/DumbIRRepeater.ino index d3ddbdb6a..2d3f1a8b3 100644 --- a/lib/IRremoteESP8266/examples/DumbIRRepeater/DumbIRRepeater.ino +++ b/lib/IRremoteESP8266/examples/DumbIRRepeater/DumbIRRepeater.ino @@ -57,7 +57,12 @@ // The GPIO an IR detector/demodulator is connected to. Recommended: 14 (D5) // Note: GPIO 16 won't work on the ESP8266 as it does not have interrupts. +// Note: GPIO 14 won't work on the ESP32-C3 as it causes the board to reboot. +#ifdef ARDUINO_ESP32C3_DEV +const uint16_t kRecvPin = 10; // 14 on a ESP32-C3 causes a boot loop. +#else // ARDUINO_ESP32C3_DEV const uint16_t kRecvPin = 14; +#endif // ARDUINO_ESP32C3_DEV // GPIO to use to control the IR LED circuit. Recommended: 4 (D2). const uint16_t kIrLedPin = 4; diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h index 1d05d3691..611c1ff73 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.h @@ -169,6 +169,11 @@ const uint32_t kMqttReconnectTime = 5000; // Delay(ms) between reconnect tries. // In theory, you shouldn't need this as you can always clean up by hand, hence // it is disabled by default. Note: `false` saves ~1.2k. #define MQTT_CLEAR_ENABLE false + +#ifndef MQTT_SERVER_AUTODETECT_ENABLE +// Whether or not MQTT Server IP is detected through mDNS +#define MQTT_SERVER_AUTODETECT_ENABLE true +#endif // MQTT_SERVER_AUTODETECT_ENABLE #endif // MQTT_ENABLE // ------------------------ IR Capture Settings -------------------------------- @@ -285,7 +290,7 @@ const uint16_t kJsonAcStateMaxSize = 1024; // Bytes // ----------------- End of User Configuration Section ------------------------- // Constants -#define _MY_VERSION_ "v1.6.0" +#define _MY_VERSION_ "v1.7.0" const uint8_t kRebootTime = 15; // Seconds const uint8_t kQuickDisplayTime = 2; // Seconds diff --git a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino index c2e427166..4f3324fc2 100644 --- a/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino +++ b/lib/IRremoteESP8266/examples/IRMQTTServer/IRMQTTServer.ino @@ -24,6 +24,10 @@ * ## Before First Boot (i.e. Compile time) * - Disable MQTT if desired. (see '#define MQTT_ENABLE' in IRMQTTServer.h). * + * - The MQTT server IP is detected automatically through mDNS (aka avahi, + * bonjour, zeroconf) if the server advertises _mqtt._tcp. Disable this if + * desired (see '#define MQTT_SERVER_AUTODETECT_ENABLE' in IRMQTTServer.h). + * * - Site specific settings: * o Search for 'CHANGE_ME' in IRMQTTServer.h for the things you probably * need to change for your particular situation. @@ -1973,6 +1977,29 @@ void handleNotFound(void) { server.send(404, "text/plain", message); } +#if (MQTT_ENABLE && MQTT_SERVER_AUTODETECT_ENABLE) +void mqtt_detect_server(void) { + debug("Looking for the MQTT server..."); + int nrOfServices = MDNS.queryService("mqtt", "tcp"); + if (nrOfServices == 0) { + debug("MQTT server not found"); + return; + } + + debug(("Number of MQTT servers found: " + String(nrOfServices)).c_str()); + for (int i = 0; i < nrOfServices; i=i+1) { + debug(("Hostname: " + MDNS.hostname(i)).c_str()); + debug(("IP address: " + MDNS.IP(i).toString()).c_str()); + debug(("Port: " + String(MDNS.port(i))).c_str()); + } + strncpy(MqttServer, MDNS.IP(0).toString().c_str(), kHostnameLength); + strncpy(MqttPort, String(MDNS.port(0)).c_str(), kPortLength); + + debug(("First one selected: " + String(MqttServer) + ":" + + String(MqttPort)).c_str()); +} +#endif // (MQTT_ENABLE && MQTT_SERVER_AUTODETECT_ENABLE)) + void setup_wifi(void) { delay(10); loadConfigFile(); @@ -2000,6 +2027,12 @@ void setup_wifi(void) { WiFiManagerParameter custom_mqtt_text( "

MQTT Broker details
"); wifiManager.addParameter(&custom_mqtt_text); +#if MQTT_SERVER_AUTODETECT_ENABLE + WiFiManagerParameter custom_mqtt_server_text( + "
NOTE: if _mqtt._tcp mDNS service is found, it will be " + "used instead of the mqtt server:port below

"); + wifiManager.addParameter(&custom_mqtt_server_text); +#endif // MQTT_SERVER_AUTODETECT_ENABLE WiFiManagerParameter custom_mqtt_server( kMqttServerKey, "mqtt server", MqttServer, kHostnameLength); wifiManager.addParameter(&custom_mqtt_server); @@ -2196,6 +2229,9 @@ void setup(void) { // Finish setup of the mqtt clent object. if (!mqtt_client.setBufferSize(kMqttBufferSize)) debug("Can't fully allocate MQTT buffer! Try a smaller value."); +#if MQTT_SERVER_AUTODETECT_ENABLE + mqtt_detect_server(); +#endif // MQTT_SERVER_AUTODETECT_ENABLE mqtt_client.setServer(MqttServer, atoi(MqttPort)); mqtt_client.setCallback(mqttCallback); // Set various variables @@ -2308,6 +2344,10 @@ bool reconnect(void) { uint16_t tries = 1; while (!mqtt_client.connected() && tries <= 3) { int connected = false; +#if MQTT_SERVER_AUTODETECT_ENABLE + mqtt_detect_server(); + mqtt_client.setServer(MqttServer, atoi(MqttPort)); +#endif // MQTT_SERVER_AUTODETECT_ENABLE // Attempt to connect debug(("Attempting MQTT connection to " + String(MqttServer) + ":" + String(MqttPort) + "... ").c_str()); @@ -2863,7 +2903,8 @@ bool sendInt(const String topic, const int32_t num, const bool retain) { bool sendBool(const String topic, const bool on, const bool retain) { #if MQTT_ENABLE mqttSentCounter++; - return mqtt_client.publish(topic.c_str(), (on ? "on" : "off"), retain); + return mqtt_client.publish(topic.c_str(), (on ? D_STR_ON : D_STR_OFF), + retain); #else // MQTT_ENABLE return true; #endif // MQTT_ENABLE @@ -2991,12 +3032,14 @@ void updateClimate(stdAc::state_t *state, const String str, } else if (str.equals(prefix + F(KEY_POWER))) { state->power = IRac::strToBool(payload.c_str()); #if MQTT_CLIMATE_HA_MODE + // When in Home Assistant mode, Power Off, means turn the Mode to Off too. if (!state->power) state->mode = stdAc::opmode_t::kOff; #endif // MQTT_CLIMATE_HA_MODE } else if (str.equals(prefix + F(KEY_MODE))) { state->mode = IRac::strToOpmode(payload.c_str()); #if MQTT_CLIMATE_HA_MODE - state->power = (state->mode != stdAc::opmode_t::kOff); + // When in Home Assistant mode, a Mode of Off, means turn the Power off too. + if (state->mode == stdAc::opmode_t::kOff) state->power = false; #endif // MQTT_CLIMATE_HA_MODE } else if (str.equals(prefix + F(KEY_TEMP))) { state->degrees = payload.toFloat(); @@ -3055,7 +3098,7 @@ bool sendClimate(const String topic_prefix, const bool retain, // Home Assistant want's these two bound together. if (prev.power != next.power || prev.mode != next.mode || forceMQTT) { success &= sendBool(topic_prefix + KEY_POWER, next.power, retain); - if (!next.power) mode_str = F("off"); + if (!next.power) mode_str = kOffStr; #else // MQTT_CLIMATE_HA_MODE // In non-Home Assistant mode, power and mode are not bound together. if (prev.power != next.power || forceMQTT) { diff --git a/lib/IRremoteESP8266/examples/IRrecvDemo/IRrecvDemo.ino b/lib/IRremoteESP8266/examples/IRrecvDemo/IRrecvDemo.ino index 2ae2e1410..743d32c4e 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDemo/IRrecvDemo.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDemo/IRrecvDemo.ino @@ -24,7 +24,12 @@ // An IR detector/demodulator is connected to GPIO pin 14(D5 on a NodeMCU // board). // Note: GPIO 16 won't work on the ESP8266 as it does not have interrupts. +// Note: GPIO 14 won't work on the ESP32-C3 as it causes the board to reboot. +#ifdef ARDUINO_ESP32C3_DEV +const uint16_t kRecvPin = 10; // 14 on a ESP32-C3 causes a boot loop. +#else // ARDUINO_ESP32C3_DEV const uint16_t kRecvPin = 14; +#endif // ARDUINO_ESP32C3_DEV IRrecv irrecv(kRecvPin); diff --git a/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino index 70dbdba42..f0bebff12 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDumpV2/IRrecvDumpV2.ino @@ -38,7 +38,12 @@ // An IR detector/demodulator is connected to GPIO pin 14 // e.g. D5 on a NodeMCU board. // Note: GPIO 16 won't work on the ESP8266 as it does not have interrupts. +// Note: GPIO 14 won't work on the ESP32-C3 as it causes the board to reboot. +#ifdef ARDUINO_ESP32C3_DEV +const uint16_t kRecvPin = 10; // 14 on a ESP32-C3 causes a boot loop. +#else // ARDUINO_ESP32C3_DEV const uint16_t kRecvPin = 14; +#endif // ARDUINO_ESP32C3_DEV // The Serial connection baud rate. // i.e. Status message will be sent to the PC at this baud rate. diff --git a/lib/IRremoteESP8266/examples/IRrecvDumpV3/IRrecvDumpV3.ino b/lib/IRremoteESP8266/examples/IRrecvDumpV3/IRrecvDumpV3.ino index 0948ae5e9..6bc054a3c 100644 --- a/lib/IRremoteESP8266/examples/IRrecvDumpV3/IRrecvDumpV3.ino +++ b/lib/IRremoteESP8266/examples/IRrecvDumpV3/IRrecvDumpV3.ino @@ -45,7 +45,12 @@ // An IR detector/demodulator is connected to GPIO pin 14 // e.g. D5 on a NodeMCU board. // Note: GPIO 16 won't work on the ESP8266 as it does not have interrupts. +// Note: GPIO 14 won't work on the ESP32-C3 as it causes the board to reboot. +#ifdef ARDUINO_ESP32C3_DEV +const uint16_t kRecvPin = 10; // 14 on a ESP32-C3 causes a boot loop. +#else // ARDUINO_ESP32C3_DEV const uint16_t kRecvPin = 14; +#endif // ARDUINO_ESP32C3_DEV // The Serial connection baud rate. // i.e. Status message will be sent to the PC at this baud rate. diff --git a/lib/IRremoteESP8266/examples/SmartIRRepeater/SmartIRRepeater.ino b/lib/IRremoteESP8266/examples/SmartIRRepeater/SmartIRRepeater.ino index d35c4cef6..10554735d 100644 --- a/lib/IRremoteESP8266/examples/SmartIRRepeater/SmartIRRepeater.ino +++ b/lib/IRremoteESP8266/examples/SmartIRRepeater/SmartIRRepeater.ino @@ -60,7 +60,12 @@ // The GPIO an IR detector/demodulator is connected to. Recommended: 14 (D5) // Note: GPIO 16 won't work on the ESP8266 as it does not have interrupts. +// Note: GPIO 14 won't work on the ESP32-C3 as it causes the board to reboot. +#ifdef ARDUINO_ESP32C3_DEV +const uint16_t kRecvPin = 10; // 14 on a ESP32-C3 causes a boot loop. +#else // ARDUINO_ESP32C3_DEV const uint16_t kRecvPin = 14; +#endif // ARDUINO_ESP32C3_DEV // GPIO to use to control the IR LED circuit. Recommended: 4 (D2). const uint16_t kIrLedPin = 4; diff --git a/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino b/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino index ebdb7536b..974bb94fc 100644 --- a/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino +++ b/lib/IRremoteESP8266/examples/TurnOnKelvinatorAC/TurnOnKelvinatorAC.ino @@ -57,7 +57,7 @@ void setup() { ac.setFan(1); ac.setMode(kKelvinatorCool); ac.setTemp(26); - ac.setSwingVertical(false); + ac.setSwingVertical(false, kKelvinatorSwingVOff); ac.setSwingHorizontal(true); ac.setXFan(true); ac.setIonFilter(false); diff --git a/lib/IRremoteESP8266/keywords.txt b/lib/IRremoteESP8266/keywords.txt index e6e33a008..bab1f5d5e 100644 --- a/lib/IRremoteESP8266/keywords.txt +++ b/lib/IRremoteESP8266/keywords.txt @@ -47,8 +47,10 @@ IRHaierAC176 KEYWORD1 IRHaierACYRW02 KEYWORD1 IRHitachiAc KEYWORD1 IRHitachiAc1 KEYWORD1 +IRHitachiAc264 KEYWORD1 +IRHitachiAc296 KEYWORD1 IRHitachiAc3 KEYWORD1 -IRHitachiAc344: KEYWORD1 +IRHitachiAc344 KEYWORD1 IRHitachiAc424 KEYWORD1 IRKelonAc KEYWORD1 IRKelvinatorAC KEYWORD1 @@ -120,6 +122,7 @@ _getTime KEYWORD2 _getTimer KEYWORD2 _isAKB73757604 KEYWORD2 _isAKB74955603 KEYWORD2 +_isLG6711A20083V KEYWORD2 _isNormal KEYWORD2 _matchGeneric KEYWORD2 _restoreState KEYWORD2 @@ -242,11 +245,13 @@ decodeHaierAC176 KEYWORD2 decodeHaierACYRW02 KEYWORD2 decodeHash KEYWORD2 decodeHitachiAC KEYWORD2 +decodeHitachiAc296 KEYWORD2 decodeHitachiAc3 KEYWORD2 decodeHitachiAc424 KEYWORD2 decodeInax KEYWORD2 decodeJVC KEYWORD2 decodeKelon KEYWORD2 +decodeKelon168 KEYWORD2 decodeKelvinator KEYWORD2 decodeLG KEYWORD2 decodeLasertag KEYWORD2 @@ -344,6 +349,7 @@ fujitsu KEYWORD2 get10CHeat KEYWORD2 get3D KEYWORD2 get8CHeat KEYWORD2 +get8CHeatToggle KEYWORD2 getAbsenseDetect KEYWORD2 getBeep KEYWORD2 getBit KEYWORD2 @@ -493,12 +499,16 @@ hasInvertedStates KEYWORD2 hasStateChanged KEYWORD2 hitachi KEYWORD2 hitachi1 KEYWORD2 +hitachi264 KEYWORD2 +hitachi296 KEYWORD2 hitachi344 KEYWORD2 hitachi424 KEYWORD2 htmlEscape KEYWORD2 initState KEYWORD2 int64ToString KEYWORD2 invertBits KEYWORD2 +is8CHeatToggle KEYWORD2 +isCleanToggle KEYWORD2 isEconoToggle KEYWORD2 isLightToggle KEYWORD2 isOffCommand KEYWORD2 @@ -508,6 +518,7 @@ isOnTimerActive KEYWORD2 isOnTimerEnabled KEYWORD2 isPowerSpecial KEYWORD2 isProtocolSupported KEYWORD2 +isQuiet KEYWORD2 isRepeat KEYWORD2 isSpecialState KEYWORD2 isSwing KEYWORD2 @@ -617,12 +628,15 @@ sendHaierACYRW02 KEYWORD2 sendHitachiAC KEYWORD2 sendHitachiAC1 KEYWORD2 sendHitachiAC2 KEYWORD2 +sendHitachiAc264 KEYWORD2 +sendHitachiAc296 KEYWORD2 sendHitachiAc3 KEYWORD2 sendHitachiAc344 KEYWORD2 sendHitachiAc424 KEYWORD2 sendInax KEYWORD2 sendJVC KEYWORD2 sendKelon KEYWORD2 +sendKelon168 KEYWORD2 sendKelvinator KEYWORD2 sendLG KEYWORD2 sendLG2 KEYWORD2 @@ -694,6 +708,7 @@ serialPrintUint64 KEYWORD2 set10CHeat KEYWORD2 set3D KEYWORD2 set8CHeat KEYWORD2 +set8CHeatToggle KEYWORD2 setAbsenseDetect KEYWORD2 setAuto KEYWORD2 setBeep KEYWORD2 @@ -983,12 +998,15 @@ DECODE_HASH LITERAL1 DECODE_HITACHI_AC LITERAL1 DECODE_HITACHI_AC1 LITERAL1 DECODE_HITACHI_AC2 LITERAL1 +DECODE_HITACHI_AC264 LITERAL1 +DECODE_HITACHI_AC296 LITERAL1 DECODE_HITACHI_AC3 LITERAL1 DECODE_HITACHI_AC344 LITERAL1 DECODE_HITACHI_AC424 LITERAL1 DECODE_INAX LITERAL1 DECODE_JVC LITERAL1 DECODE_KELON LITERAL1 +DECODE_KELON168 LITERAL1 DECODE_KELVINATOR LITERAL1 DECODE_LASERTAG LITERAL1 DECODE_LEGOPF LITERAL1 @@ -1177,6 +1195,10 @@ HITACHI_AC1 LITERAL1 HITACHI_AC1_BITS LITERAL1 HITACHI_AC1_STATE_LENGTH LITERAL1 HITACHI_AC2 LITERAL1 +HITACHI_AC264 LITERAL1 +HITACHI_AC296 LITERAL1 +HITACHI_AC296_BITS LITERAL1 +HITACHI_AC296_STATE_LENGTH LITERAL1 HITACHI_AC2_BITS LITERAL1 HITACHI_AC2_STATE_LENGTH LITERAL1 HITACHI_AC3 LITERAL1 @@ -1184,11 +1206,12 @@ HITACHI_AC344 LITERAL1 HITACHI_AC424 LITERAL1 HITACHI_AC_BITS LITERAL1 HITACHI_AC_STATE_LENGTH LITERAL1 -IRAM_ATTR LITERAL1 +ICACHE_RAM_ATTR LITERAL1 INAX LITERAL1 JVC LITERAL1 JVC_BITS LITERAL1 KELON LITERAL1 +KELON168 LITERAL1 KELVINATOR LITERAL1 KELVINATOR_AUTO LITERAL1 KELVINATOR_AUTO_TEMP LITERAL1 @@ -1210,6 +1233,7 @@ LEGOPF LITERAL1 LG LITERAL1 LG2 LITERAL1 LG32_BITS LITERAL1 +LG6711A20083V LITERAL1 LG_BITS LITERAL1 LOW LITERAL1 LUTRON LITERAL1 @@ -1337,12 +1361,15 @@ SEND_HAIER_AC_YRW02 LITERAL1 SEND_HITACHI_AC LITERAL1 SEND_HITACHI_AC1 LITERAL1 SEND_HITACHI_AC2 LITERAL1 +SEND_HITACHI_AC264 LITERAL1 +SEND_HITACHI_AC296 LITERAL1 SEND_HITACHI_AC3 LITERAL1 SEND_HITACHI_AC344 LITERAL1 SEND_HITACHI_AC424 LITERAL1 SEND_INAX LITERAL1 SEND_JVC LITERAL1 SEND_KELON LITERAL1 +SEND_KELON168 LITERAL1 SEND_KELVINATOR LITERAL1 SEND_LASERTAG LITERAL1 SEND_LEGOPF LITERAL1 @@ -1685,7 +1712,6 @@ kCool LITERAL1 kCoolStr LITERAL1 kCoolingStr LITERAL1 kCoolix48Bits LITERAL1 -kCoolix48ExtraTolerance LITERAL1 kCoolixAuto LITERAL1 kCoolixBitMark LITERAL1 kCoolixBitMarkTicks LITERAL1 @@ -1696,6 +1722,7 @@ kCoolixCool LITERAL1 kCoolixDefaultRepeat LITERAL1 kCoolixDefaultState LITERAL1 kCoolixDry LITERAL1 +kCoolixExtraTolerance LITERAL1 kCoolixFan LITERAL1 kCoolixFanAuto LITERAL1 kCoolixFanAuto0 LITERAL1 @@ -2426,6 +2453,44 @@ kHitachiAc1TempAuto LITERAL1 kHitachiAc1TempDelta LITERAL1 kHitachiAc1TempSize LITERAL1 kHitachiAc1TimerSize LITERAL1 +kHitachiAc264Bits LITERAL1 +kHitachiAc264ButtonFan LITERAL1 +kHitachiAc264ButtonPowerMode LITERAL1 +kHitachiAc264ButtonSwingV LITERAL1 +kHitachiAc264ButtonTempDown LITERAL1 +kHitachiAc264ButtonTempUp LITERAL1 +kHitachiAc264Cool LITERAL1 +kHitachiAc264Dry LITERAL1 +kHitachiAc264Fan LITERAL1 +kHitachiAc264FanAuto LITERAL1 +kHitachiAc264FanHigh LITERAL1 +kHitachiAc264FanLow LITERAL1 +kHitachiAc264FanMedium LITERAL1 +kHitachiAc264FanMin LITERAL1 +kHitachiAc264Heat LITERAL1 +kHitachiAc264MaxTemp LITERAL1 +kHitachiAc264MinTemp LITERAL1 +kHitachiAc264StateLength LITERAL1 +kHitachiAc296Auto LITERAL1 +kHitachiAc296AutoDehumidifying LITERAL1 +kHitachiAc296Bits LITERAL1 +kHitachiAc296CondensationControl LITERAL1 +kHitachiAc296Cool LITERAL1 +kHitachiAc296Dehumidify LITERAL1 +kHitachiAc296DryCool LITERAL1 +kHitachiAc296FanAuto LITERAL1 +kHitachiAc296FanHigh LITERAL1 +kHitachiAc296FanLow LITERAL1 +kHitachiAc296FanMedium LITERAL1 +kHitachiAc296FanSilent LITERAL1 +kHitachiAc296Heat LITERAL1 +kHitachiAc296MaxTemp LITERAL1 +kHitachiAc296MinTemp LITERAL1 +kHitachiAc296PowerOff LITERAL1 +kHitachiAc296PowerOn LITERAL1 +kHitachiAc296QuickLaundry LITERAL1 +kHitachiAc296StateLength LITERAL1 +kHitachiAc296TempAuto LITERAL1 kHitachiAc2Bits LITERAL1 kHitachiAc2StateLength LITERAL1 kHitachiAc344Bits LITERAL1 @@ -2490,8 +2555,6 @@ kHitachiAc424LdrSpace LITERAL1 kHitachiAc424MaxTemp LITERAL1 kHitachiAc424MinTemp LITERAL1 kHitachiAc424OneSpace LITERAL1 -kHitachiAc424PowerOff LITERAL1 -kHitachiAc424PowerOn LITERAL1 kHitachiAc424StateLength LITERAL1 kHitachiAc424ZeroSpace LITERAL1 kHitachiAcAuto LITERAL1 @@ -2553,6 +2616,12 @@ kJvcRptLengthTicks LITERAL1 kJvcTick LITERAL1 kJvcZeroSpace LITERAL1 kJvcZeroSpaceTicks LITERAL1 +kKelon168Bits LITERAL1 +kKelon168FooterSpace LITERAL1 +kKelon168Section1Size LITERAL1 +kKelon168Section2Size LITERAL1 +kKelon168Section3Size LITERAL1 +kKelon168StateLength LITERAL1 kKelonBitMark LITERAL1 kKelonBits LITERAL1 kKelonDryGradeMax LITERAL1 @@ -2602,6 +2671,16 @@ kKelvinatorMinTemp LITERAL1 kKelvinatorOneSpace LITERAL1 kKelvinatorOneSpaceTicks LITERAL1 kKelvinatorStateLength LITERAL1 +kKelvinatorSwingVAuto LITERAL1 +kKelvinatorSwingVHighAuto LITERAL1 +kKelvinatorSwingVHighest LITERAL1 +kKelvinatorSwingVLowAuto LITERAL1 +kKelvinatorSwingVLowerMiddle LITERAL1 +kKelvinatorSwingVLowest LITERAL1 +kKelvinatorSwingVMiddle LITERAL1 +kKelvinatorSwingVMiddleAuto LITERAL1 +kKelvinatorSwingVOff LITERAL1 +kKelvinatorSwingVUpperMiddle LITERAL1 kKelvinatorTick LITERAL1 kKelvinatorZeroSpace LITERAL1 kKelvinatorZeroSpaceTicks LITERAL1 @@ -2640,6 +2719,7 @@ kLg32Bits LITERAL1 kLg32HdrMark LITERAL1 kLg32HdrSpace LITERAL1 kLg32RptHdrMark LITERAL1 +kLg6711a20083vStr LITERAL1 kLgAcAKB74955603DetectionMask LITERAL1 kLgAcAuto LITERAL1 kLgAcChecksumSize LITERAL1 @@ -2684,6 +2764,7 @@ kLgAcSwingVOff LITERAL1 kLgAcSwingVOff_Short LITERAL1 kLgAcSwingVSwing LITERAL1 kLgAcSwingVSwing_Short LITERAL1 +kLgAcSwingVToggle LITERAL1 kLgAcSwingVUpperMiddle LITERAL1 kLgAcSwingVUpperMiddle_Short LITERAL1 kLgAcTempAdjust LITERAL1 @@ -2786,10 +2867,14 @@ kMideaACMinSensorTempC LITERAL1 kMideaACMinSensorTempF LITERAL1 kMideaACMinTempC LITERAL1 kMideaACMinTempF LITERAL1 +kMideaACQuietOff LITERAL1 +kMideaACQuietOn LITERAL1 kMideaACSensorTempOnTimerOff LITERAL1 kMideaACTimerOff LITERAL1 +kMideaACToggle8CHeat LITERAL1 kMideaACToggleEcono LITERAL1 kMideaACToggleLight LITERAL1 +kMideaACToggleSelfClean LITERAL1 kMideaACToggleTurbo LITERAL1 kMideaACTypeCommand LITERAL1 kMideaACTypeFollow LITERAL1 diff --git a/lib/IRremoteESP8266/library.json b/lib/IRremoteESP8266/library.json index e1777f1c4..948ae48fa 100644 --- a/lib/IRremoteESP8266/library.json +++ b/lib/IRremoteESP8266/library.json @@ -1,6 +1,6 @@ { "name": "IRremoteESP8266", - "version": "2.8.1", + "version": "2.8.2", "keywords": "infrared, ir, remote, esp8266, esp32", "description": "Send and receive infrared signals with multiple protocols (ESP8266/ESP32)", "repository": diff --git a/lib/IRremoteESP8266/library.properties b/lib/IRremoteESP8266/library.properties index 5ee1f5229..d776853f2 100644 --- a/lib/IRremoteESP8266/library.properties +++ b/lib/IRremoteESP8266/library.properties @@ -1,5 +1,5 @@ name=IRremoteESP8266 -version=2.8.1 +version=2.8.2 author=David Conran, Sebastien Warin, Mark Szabo, Ken Shirriff maintainer=David Conran, Mark Szabo, Sebastien Warin, Roi Dayan, Massimiliano Pinto, Christian Nilsson sentence=Send and receive infrared signals with multiple protocols (ESP8266/ESP32) diff --git a/lib/IRremoteESP8266/src/IRac.cpp b/lib/IRremoteESP8266/src/IRac.cpp index f9d9647d3..e14f05b62 100644 --- a/lib/IRremoteESP8266/src/IRac.cpp +++ b/lib/IRremoteESP8266/src/IRac.cpp @@ -71,7 +71,6 @@ IRac::IRac(const uint16_t pin, const bool inverted, const bool use_modulation) { _pin = pin; _inverted = inverted; _modulation = use_modulation; - initState(&next); this->markAsSent(); } @@ -132,11 +131,8 @@ void IRac::initState(stdAc::state_t *state, /// @param[out] state A Ptr to where the settings will be stored. /// @note Sets all the parameters to reasonable base/automatic defaults. void IRac::initState(stdAc::state_t *state) { - initState(state, decode_type_t::UNKNOWN, -1, false, stdAc::opmode_t::kOff, - 25, true, // 25 degrees Celsius - stdAc::fanspeed_t::kAuto, stdAc::swingv_t::kOff, - stdAc::swingh_t::kOff, false, false, false, false, false, false, - false, -1, -1); + stdAc::state_t def; + *state = def; } /// Get the current internal A/C climate state. @@ -231,6 +227,12 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { #if SEND_HITACHI_AC1 case decode_type_t::HITACHI_AC1: #endif +#if SEND_HITACHI_AC264 + case decode_type_t::HITACHI_AC264: +#endif +#if SEND_HITACHI_AC296 + case decode_type_t::HITACHI_AC296: +#endif #if SEND_HITACHI_AC344 case decode_type_t::HITACHI_AC344: #endif @@ -1313,6 +1315,64 @@ void IRac::hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model, } #endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC264 +/// Send a Hitachi 264-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc264 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +void IRac::hitachi264(IRHitachiAc264 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setPower(on); + // No Swing(V) setting available. + // No Swing(H) 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_HITACHI_AC264 + +#if SEND_HITACHI_AC296 +/// Send a Hitachi 296-bit A/C message with the supplied settings. +/// @param[in, out] ac A Ptr to an IRHitachiAc296 object to use. +/// @param[in] on The power setting. +/// @param[in] mode The operation mode setting. +/// @param[in] degrees The temperature setting in degrees. +/// @param[in] fan The speed setting for the fan. +void IRac::hitachi296(IRHitachiAc296 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan) { + ac->begin(); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setPower(on); + // No Swing(V) setting available. + // No Swing(H) 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_HITACHI_AC296 + #if SEND_HITACHI_AC344 /// Send a Hitachi 344-bit A/C message with the supplied settings. /// @param[in, out] ac A Ptr to an IRHitachiAc344 object to use. @@ -1437,7 +1497,8 @@ void IRac::kelvinator(IRKelvinatorAC *ac, ac->setMode(ac->convertMode(mode)); ac->setTemp(degrees); ac->setFan((uint8_t)fan); // No conversion needed. - ac->setSwingVertical((int8_t)swingv >= 0); + ac->setSwingVertical(swingv == stdAc::swingv_t::kAuto, // Set auto flag. + ac->convertSwingV(swingv)); ac->setSwingHorizontal((int8_t)swingh >= 0); ac->setQuiet(quiet); ac->setTurbo(turbo); @@ -1480,6 +1541,12 @@ void IRac::lg(IRLgAc *ac, const lg_ac_remote_model_t model, const uint8_t pos = ac->convertVaneSwingV(swingv); for (uint8_t vane = 0; vane < kLgAcSwingVMaxVanes; vane++) ac->setVaneSwingV(vane, pos); + // Toggle the swingv for LG6711A20083V models if we need to. + // i.e. Off to Not-Off, send a toggle. Not-Off to Off, send a toggle. + if ((model == lg_ac_remote_model_t::LG6711A20083V) && + ((swingv == stdAc::swingv_t::kOff) != + (swingv_prev == stdAc::swingv_t::kOff))) + ac->setSwingV(kLgAcSwingVToggle); ac->setSwingH(swingh != stdAc::swingh_t::kOff); // No Quiet setting available. // No Turbo setting available. @@ -1502,16 +1569,21 @@ void IRac::lg(IRLgAc *ac, const lg_ac_remote_model_t model, /// @param[in] degrees The temperature setting in degrees. /// @param[in] fan The speed setting for the fan. /// @param[in] swingv The vertical swing setting. +/// @param[in] quiet Run the device in quiet/silent mode. +/// @param[in] quiet_prev The device's previous quiet/silent mode. /// @param[in] turbo Toggle the device's turbo/powerful mode. /// @param[in] econo Toggle the device's economical mode. /// @param[in] light Toggle the LED/Display mode. +/// @param[in] clean Turn on the self-cleaning mode. e.g. XFan, dry filters etc /// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on. /// @note On Danby A/C units, swingv controls the Ion Filter instead. 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 bool turbo, - const bool econo, const bool light, const int16_t sleep) { + const stdAc::swingv_t swingv, + const bool quiet, const bool quiet_prev, + const bool turbo, const bool econo, const bool light, + const bool clean, const int16_t sleep) { ac->begin(); ac->setPower(on); ac->setMode(ac->convertMode(mode)); @@ -1520,12 +1592,12 @@ void IRac::midea(IRMideaAC *ac, ac->setFan(ac->convertFan(fan)); ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff); // No Horizontal swing setting available. - // No Quiet setting available. + ac->setQuiet(quiet, quiet_prev); ac->setTurboToggle(turbo); ac->setEconoToggle(econo); ac->setLightToggle(light); // No Filter setting available. - // No Clean setting available. + ac->setCleanToggle(clean); // No Beep setting available. ac->setSleep(sleep >= 0); // Sleep on this A/C is either on or off. // No Clock setting available. @@ -1570,6 +1642,7 @@ void IRac::mitsubishi(IRMitsubishiAC *ac, ac->setTemp(degrees); ac->setFan(ac->convertFan(fan)); ac->setVane(ac->convertSwingV(swingv)); + ac->setVaneLeft(ac->convertSwingV(swingv)); ac->setWideVane(ac->convertSwingH(swingh)); if (quiet) ac->setFan(kMitsubishiAcFanSilent); ac->setISave10C(false); @@ -2530,6 +2603,7 @@ stdAc::state_t IRac::handleToggles(const stdAc::state_t desired, result.turbo = desired.turbo ^ prev->turbo; result.econo = desired.econo ^ prev->econo; result.light = desired.light ^ prev->light; + result.clean = desired.clean ^ prev->clean; // FALL THRU case decode_type_t::CORONA_AC: case decode_type_t::HITACHI_AC344: @@ -2645,6 +2719,9 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { const stdAc::swingv_t prev_swingv = (prev != NULL) ? prev->swingv : stdAc::swingv_t::kOff; #endif // (SEND_LG || SEND_SHARP_AC) +#if SEND_MIDEA + const bool prev_quiet = (prev != NULL) ? prev->quiet : !send.quiet; +#endif // SEND_MIDEA // Per vendor settings & setup. switch (send.protocol) { #if SEND_AIRTON @@ -2894,6 +2971,22 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { break; } #endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC264 + case HITACHI_AC264: + { + IRHitachiAc264 ac(_pin, _inverted, _modulation); + hitachi264(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + case HITACHI_AC296: + { + IRHitachiAc296 ac(_pin, _inverted, _modulation); + hitachi296(&ac, send.power, send.mode, degC, send.fanspeed); + break; + } +#endif // SEND_HITACHI_AC296 #if SEND_HITACHI_AC344 case HITACHI_AC344: { @@ -2945,8 +3038,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { { IRMideaAC ac(_pin, _inverted, _modulation); midea(&ac, send.power, send.mode, send.celsius, send.degrees, - send.fanspeed, send.swingv, send.turbo, send.econo, send.light, - send.sleep); + send.fanspeed, send.swingv, send.quiet, prev_quiet, send.turbo, + send.econo, send.light, send.sleep); break; } #endif // SEND_MIDEA @@ -3380,6 +3473,8 @@ int16_t IRac::strToModel(const char *str, const int16_t def) { return gree_ac_remote_model_t::YAW1F; } else if (!STRCASECMP(str, kYbofbStr)) { return gree_ac_remote_model_t::YBOFB; + } else if (!STRCASECMP(str, kYx1fsfStr)) { + return gree_ac_remote_model_t::YX1FSF; // Haier models } else if (!STRCASECMP(str, kV9014557AStr)) { return haier_ac176_remote_model_t::V9014557_A; @@ -3412,6 +3507,8 @@ int16_t IRac::strToModel(const char *str, const int16_t def) { return lg_ac_remote_model_t::AKB74955603; } else if (!STRCASECMP(str, kAkb73757604Str)) { return lg_ac_remote_model_t::AKB73757604; + } else if (!STRCASECMP(str, kLg6711a20083vStr)) { + return lg_ac_remote_model_t::LG6711A20083V; // Panasonic A/C families } else if (!STRCASECMP(str, kLkeStr) || !STRCASECMP(str, kPanasonicLkeStr)) { @@ -3747,6 +3844,20 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_HITACHI_AC1 +#if DECODE_HITACHI_AC264 + case decode_type_t::HITACHI_AC264: { + IRHitachiAc264 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC264 +#if DECODE_HITACHI_AC296 + case decode_type_t::HITACHI_AC296: { + IRHitachiAc296 ac(kGpioUnused); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_HITACHI_AC296 #if DECODE_HITACHI_AC344 case decode_type_t::HITACHI_AC344: { IRHitachiAc344 ac(kGpioUnused); @@ -4060,7 +4171,7 @@ namespace IRAcUtils { case decode_type_t::DAIKIN128: { IRDaikin128 ac(kGpioUnused); ac.setRaw(decode->state); - *result = ac.toCommon(); + *result = ac.toCommon(prev); break; } #endif // DECODE_DAIKIN128 @@ -4144,7 +4255,7 @@ namespace IRAcUtils { case decode_type_t::FUJITSU_AC: { IRFujitsuAC ac(kGpioUnused); ac.setRaw(decode->state, decode->bits / 8); - *result = ac.toCommon(); + *result = ac.toCommon(prev); break; } #endif // DECODE_FUJITSU_AC @@ -4204,6 +4315,22 @@ namespace IRAcUtils { break; } #endif // DECODE_HITACHI_AC1 +#if DECODE_HITACHI_AC264 + case decode_type_t::HITACHI_AC264: { + IRHitachiAc264 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC264 +#if DECODE_HITACHI_AC296 + case decode_type_t::HITACHI_AC296: { + IRHitachiAc296 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_HITACHI_AC296 #if DECODE_HITACHI_AC344 case decode_type_t::HITACHI_AC344: { IRHitachiAc344 ac(kGpioUnused); @@ -4224,7 +4351,7 @@ namespace IRAcUtils { case decode_type_t::KELON: { IRKelonAc ac(kGpioUnused); ac.setRaw(decode->value); - *result = ac.toCommon(); + *result = ac.toCommon(prev); break; } #endif // DECODE_KELON diff --git a/lib/IRremoteESP8266/src/IRac.h b/lib/IRremoteESP8266/src/IRac.h index 9954eecb0..8e3ef7582 100644 --- a/lib/IRremoteESP8266/src/IRac.h +++ b/lib/IRremoteESP8266/src/IRac.h @@ -300,6 +300,16 @@ void electra(IRElectraAc *ac, const stdAc::swingv_t swingv, const stdAc::swingh_t swingh, const bool swing_toggle, const int16_t sleep = -1); #endif // SEND_HITACHI_AC1 +#if SEND_HITACHI_AC264 + void hitachi264(IRHitachiAc264 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan); +#endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + void hitachi296(IRHitachiAc296 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan); +#endif // SEND_HITACHI_AC296 #if SEND_HITACHI_AC344 void hitachi344(IRHitachiAc344 *ac, const bool on, const stdAc::opmode_t mode, @@ -338,8 +348,10 @@ void electra(IRElectraAc *ac, void 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 bool turbo, const bool econo, - const bool light, const int16_t sleep = -1); + const stdAc::swingv_t swingv, + const bool quiet, const bool quiet_prev, const bool turbo, + const bool econo, const bool light, const bool clean, + const int16_t sleep = -1); #endif // SEND_MIDEA #if SEND_MIRAGE void mirage(IRMirageAc *ac, const stdAc::state_t state); diff --git a/lib/IRremoteESP8266/src/IRrecv.cpp b/lib/IRremoteESP8266/src/IRrecv.cpp index 0b2efeb09..6ff9fd70b 100644 --- a/lib/IRremoteESP8266/src/IRrecv.cpp +++ b/lib/IRremoteESP8266/src/IRrecv.cpp @@ -22,8 +22,8 @@ extern "C" { #include "IRutils.h" #ifdef UNIT_TEST -#undef IRAM_ATTR -#define IRAM_ATTR +#undef ICACHE_RAM_ATTR +#define ICACHE_RAM_ATTR #endif #ifndef USE_IRAM_ATTR @@ -261,13 +261,20 @@ static void USE_IRAM_ATTR gpio_intr() { /// capturing data. (Default: kTimeoutMs) /// @param[in] save_buffer Use a second (save) buffer to decode from. /// (Default: false) -/// @param[in] timer_num Nr. of the ESP32 timer to use (0 to 3) (ESP32 Only) +/// @param[in] timer_num Nr. of the ESP32 timer to use. (0 to 3) (ESP32 Only) +/// or (0 to 1) (ESP32-C3) #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); + // Ensure we use a valid timer number. + _timer_num = std::min(timer_num, + (uint8_t)( +#ifdef SOC_TIMER_GROUP_TOTAL_TIMERS + SOC_TIMER_GROUP_TOTAL_TIMERS - 1)); +#else // SOC_TIMER_GROUP_TOTAL_TIMERS + 3)); +#endif // SOC_TIMER_GROUP_TOTAL_TIMERS #else // ESP32 /// @cond IGNORE /// Class constructor @@ -353,6 +360,13 @@ void IRrecv::enableIRIn(const bool pullup) { // Initialise the ESP32 timer. // 80MHz / 80 = 1 uSec granularity. timer = timerBegin(_timer_num, 80, true); +#ifdef DEBUG + if (timer == NULL) { + DPRINT("FATAL: Unable enable system timer: "); + DPRINTLN((uint16_t)_timer_num); + } +#endif // DEBUG + assert(timer != NULL); // Check we actually got the timer. // Set the timer so it only fires once, and set it's trigger in uSeconds. timerAlarmWrite(timer, MS_TO_USEC(params.timeout), ONCE); // Note: Interrupt needs to be attached before it can be enabled or disabled. @@ -715,6 +729,11 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting Sharp decode"); if (decodeSharp(results, offset)) return true; #endif +#if DECODE_BOSCH144 + DPRINTLN("Attempting Bosch 144-bit decode"); + // Bosch is similar to Coolix, so it must be attempted before decodeCOOLIX. + if (decodeBosch144(results, offset)) return true; +#endif // DECODE_BOSCH144 #if DECODE_COOLIX DPRINTLN("Attempting Coolix 24-bit decode"); if (decodeCOOLIX(results, offset)) return true; @@ -830,6 +849,18 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, if (decodeHitachiAC(results, offset, kHitachiAc344Bits, true, false)) return true; #endif // DECODE_HITACHI_AC344 +#if DECODE_HITACHI_AC264 + // HitachiAC264 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC264 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc264Bits, true, false)) + return true; +#endif // DECODE_HITACHI_AC264 +#if DECODE_HITACHI_AC296 + // HitachiAC296 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC296 decode"); + if (decodeHitachiAc296(results, offset, kHitachiAc296Bits, true)) + return true; +#endif // DECODE_HITACHI_AC296 #if DECODE_HITACHI_AC2 // HitachiAC2 should be checked before HitachiAC DPRINTLN("Attempting Hitachi AC2 decode"); @@ -1044,8 +1075,12 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting Teknopoint decode"); if (decodeTeknopoint(results, offset)) return true; #endif // DECODE_TEKNOPOINT +#if DECODE_KELON168 + DPRINTLN("Attempting Kelon 168-bit decode"); + if (decodeKelon168(results, offset)) return true; +#endif // DECODE_KELON168 #if DECODE_KELON - DPRINTLN("Attempting Kelon decode"); + DPRINTLN("Attempting Kelon 48-bit decode"); if (decodeKelon(results, offset)) return true; #endif // DECODE_KELON #if DECODE_SANYO_AC88 @@ -1072,6 +1107,39 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting Coolix 48-bit decode"); if (decodeCoolix48(results, offset)) return true; #endif // DECODE_COOLIX48 +#if DECODE_DAIKIN200 + DPRINTLN("Attempting Daikin 200-bit decode"); + if (decodeDaikin200(results, offset)) return true; +#endif // DECODE_DAIKIN200 +#if DECODE_HAIER_AC160 + DPRINTLN("Attempting Haier AC 160 bit decode"); + if (decodeHaierAC160(results, offset)) return true; +#endif // DECODE_HAIER_AC160 +#if DECODE_CARRIER_AC128 + DPRINTLN("Attempting Carrier AC 128-bit decode"); + if (decodeCarrierAC128(results, offset)) return true; +#endif // DECODE_CARRIER_AC128 +#if DECODE_TOTO + DPRINTLN("Attempting Toto 48/24-bit decode"); + if (decodeToto(results, offset, kTotoLongBits) || // Long needs to be first + decodeToto(results, offset, kTotoShortBits)) return true; +#endif // DECODE_TOTO +#if DECODE_CLIMABUTLER + DPRINTLN("Attempting ClimaButler decode"); + if (decodeClimaButler(results)) return true; +#endif // DECODE_CLIMABUTLER +#if DECODE_TCL96AC + DPRINTLN("Attempting TCL AC 96-bit decode"); + if (decodeTcl96Ac(results, offset)) return true; +#endif // DECODE_TCL96AC +#if DECODE_SANYO_AC152 + DPRINTLN("Attempting Sanyo AC 152-bit decode"); + if (decodeSanyoAc152(results, offset)) return true; +#endif // DECODE_SANYO_AC152 +#if DECODE_DAIKIN312 + DPRINTLN("Attempting Daikin 312-bit decode"); + if (decodeDaikin312(results, offset)) return true; +#endif // DECODE_DAIKIN312 // Typically new protocols are added above this line. } #if DECODE_HASH @@ -1086,7 +1154,7 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, if (!resumed) // Check if we have already resumed. resume(); return false; -} +} // NOLINT(readability/fn_size) /// Convert the tolerance percentage into something valid. /// @param[in] percentage An integer percentage. @@ -1166,8 +1234,8 @@ bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, DPRINT(". Matching: "); DPRINT(measured); DPRINT(" >= "); - DPRINT(ticksLow(std::min(desired, MS_TO_USEC(params.timeout)), tolerance, - delta)); + DPRINT(ticksLow(std::min(desired, (uint32_t)MS_TO_USEC(params.timeout)), + tolerance, delta)); DPRINT(" [min("); DPRINT(ticksLow(desired, tolerance, delta)); DPRINT(", "); diff --git a/lib/IRremoteESP8266/src/IRrecv.h b/lib/IRremoteESP8266/src/IRrecv.h index 75070172d..025de5272 100644 --- a/lib/IRremoteESP8266/src/IRrecv.h +++ b/lib/IRremoteESP8266/src/IRrecv.h @@ -52,8 +52,15 @@ 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) +#ifdef ESP32 +// Which of the ESP32 timers to use by default. +// (3 for most ESP32s, 1 for ESP32-C3s) +#ifdef SOC_TIMER_GROUP_TOTAL_TIMERS +const uint8_t kDefaultESP32Timer = SOC_TIMER_GROUP_TOTAL_TIMERS - 1; +#else // SOC_TIMER_GROUP_TOTAL_TIMERS const uint8_t kDefaultESP32Timer = 3; +#endif // SOC_TIMER_GROUP_TOTAL_TIMERS +#endif // ESP32 #if DECODE_AC // Hitachi AC is the current largest state size. @@ -318,6 +325,12 @@ class IRrecv { const uint16_t nbits = kSanyoAc88Bits, const bool strict = true); #endif // DECODE_SANYO_AC88 +#if DECODE_SANYO_AC152 + bool decodeSanyoAc152(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kSanyoAc152Bits, + const bool strict = true); +#endif // DECODE_SANYO_AC152 #if DECODE_MITSUBISHI bool decodeMitsubishi(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kMitsubishiBits, @@ -500,11 +513,21 @@ class IRrecv { const uint16_t nbits = kDaikin2Bits, const bool strict = true); #endif +#if DECODE_DAIKIN200 + bool decodeDaikin200(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin200Bits, + const bool strict = true); +#endif // DECODE_DAIKIN200 #if DECODE_DAIKIN216 bool decodeDaikin216(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kDaikin216Bits, const bool strict = true); -#endif +#endif // DECODE_DAIKIN216 +#if DECODE_DAIKIN312 + bool decodeDaikin312(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDaikin312Bits, + const bool strict = true); +#endif // DECODE_DAIKIN312 #if DECODE_TOSHIBA_AC bool decodeToshibaAC(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kToshibaACBits, @@ -562,6 +585,12 @@ class IRrecv { const uint16_t nbits = kCarrierAc64Bits, const bool strict = true); #endif // DECODE_CARRIER_AC64 +#if DECODE_CARRIER_AC128 + bool decodeCarrierAC128(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kCarrierAc128Bits, + const bool strict = true); +#endif // DECODE_CARRIER_AC128 #if DECODE_GOODWEATHER bool decodeGoodweather(decode_results *results, uint16_t offset = kStartOffset, @@ -573,7 +602,8 @@ class IRrecv { const uint16_t nbits = kGreeBits, const bool strict = true); #endif -#if (DECODE_HAIER_AC | DECODE_HAIER_AC_YRW02) +#if (DECODE_HAIER_AC | DECODE_HAIER_AC_YRW02 || DECODE_HAIER_AC160 || \ + DECODE_HAIER_AC176) bool decodeHaierAC(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kHaierACBits, const bool strict = true); @@ -584,17 +614,25 @@ class IRrecv { const uint16_t nbits = kHaierACYRW02Bits, const bool strict = true); #endif +#if DECODE_HAIER_AC160 + bool decodeHaierAC160(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHaierAC160Bits, + const bool strict = true); +#endif // DECODE_HAIER_AC160 #if DECODE_HAIER_AC176 bool decodeHaierAC176(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kHaierAC176Bits, const bool strict = true); #endif // DECODE_HAIER_AC176 -#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC344) +#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || \ + DECODE_HITACHI_AC344) bool decodeHitachiAC(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kHitachiAcBits, const bool strict = true, const bool MSBfirst = true); -#endif +#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || + // DECODE_HITACHI_AC344) #if DECODE_HITACHI_AC1 bool decodeHitachiAC1(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kHitachiAc1Bits, @@ -606,6 +644,12 @@ class IRrecv { const uint16_t nbits = kHitachiAc3Bits, const bool strict = true); #endif // DECODE_HITACHI_AC3 +#if DECODE_HITACHI_AC296 + bool decodeHitachiAc296(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAc296Bits, + const bool strict = true); +#endif // DECODE_HITACHI_AC296 #if DECODE_HITACHI_AC424 bool decodeHitachiAc424(decode_results *results, uint16_t offset = kStartOffset, @@ -771,6 +815,11 @@ class IRrecv { bool decodeKelon(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kKelonBits, const bool strict = true); #endif // DECODE_KELON +#if DECODE_KELON168 + bool decodeKelon168(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kKelon168Bits, + const bool strict = true); +#endif // DECODE_KELON168 #if DECODE_BOSE bool decodeBose(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kBoseBits, const bool strict = true); @@ -784,6 +833,29 @@ class IRrecv { const uint16_t nbits = kAirtonBits, const bool strict = true); #endif // DECODE_AIRTON +#if DECODE_TOTO + bool decodeToto(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kTotoBits, + const bool strict = true); +#endif // DECODE_TOTO +#if DECODE_CLIMABUTLER + bool decodeClimaButler(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kClimaButlerBits, + const bool strict = true); +#endif // DECODE_CLIMABUTLER +#if DECODE_TCL96AC + bool decodeTcl96Ac(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kTcl96AcBits, + const bool strict = true); +#endif // DECODE_TCL96AC +#if DECODE_BOSCH144 + bool decodeBosch144(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kBosch144Bits, + const bool strict = true); +#endif // DECODE_BOSCH144 }; #endif // IRRECV_H_ diff --git a/lib/IRremoteESP8266/src/IRremoteESP8266.h b/lib/IRremoteESP8266/src/IRremoteESP8266.h index 3cd19a81d..f73909054 100644 --- a/lib/IRremoteESP8266/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266/src/IRremoteESP8266.h @@ -58,12 +58,13 @@ // Minor version number (x.X.x) #define _IRREMOTEESP8266_VERSION_MINOR 8 // Patch version number (x.x.X) -#define _IRREMOTEESP8266_VERSION_PATCH 1 +#define _IRREMOTEESP8266_VERSION_PATCH 2 // Macro to convert version info into an integer #define _IRREMOTEESP8266_VERSION_VAL(major, minor, patch) \ ((major << 16) | (minor << 8) | (patch)) // Macro to convert literal into a string -#define MKSTR(x) #x +#define MKSTR_HELPER(x) #x +#define MKSTR(x) MKSTR_HELPER(x) // Integer version #define _IRREMOTEESP8266_VERSION _IRREMOTEESP8266_VERSION_VAL(\ _IRREMOTEESP8266_VERSION_MAJOR, \ @@ -237,6 +238,13 @@ #define SEND_SANYO_AC88 _IR_ENABLE_DEFAULT_ #endif // SEND_SANYO_AC88 +#ifndef DECODE_SANYO_AC152 +#define DECODE_SANYO_AC152 _IR_ENABLE_DEFAULT_ +#endif // DECODE_SANYO_AC152 +#ifndef SEND_SANYO_AC152 +#define SEND_SANYO_AC152 _IR_ENABLE_DEFAULT_ +#endif // SEND_SANYO_AC152 + #ifndef DECODE_MITSUBISHI #define DECODE_MITSUBISHI _IR_ENABLE_DEFAULT_ #endif // DECODE_MITSUBISHI @@ -454,6 +462,13 @@ #define SEND_CARRIER_AC64 _IR_ENABLE_DEFAULT_ #endif // SEND_CARRIER_AC64 +#ifndef DECODE_CARRIER_AC128 +#define DECODE_CARRIER_AC128 _IR_ENABLE_DEFAULT_ +#endif // DECODE_CARRIER_AC128 +#ifndef SEND_CARRIER_AC128 +#define SEND_CARRIER_AC128 _IR_ENABLE_DEFAULT_ +#endif // SEND_CARRIER_AC128 + #ifndef DECODE_HAIER_AC #define DECODE_HAIER_AC _IR_ENABLE_DEFAULT_ #endif // DECODE_HAIER_AC @@ -489,6 +504,20 @@ #define SEND_HITACHI_AC3 _IR_ENABLE_DEFAULT_ #endif // SEND_HITACHI_AC3 +#ifndef DECODE_HITACHI_AC264 +#define DECODE_HITACHI_AC264 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC264 +#ifndef SEND_HITACHI_AC264 +#define SEND_HITACHI_AC264 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC264 + +#ifndef DECODE_HITACHI_AC296 +#define DECODE_HITACHI_AC296 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC296 +#ifndef SEND_HITACHI_AC296 +#define SEND_HITACHI_AC296 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC296 + #ifndef DECODE_HITACHI_AC344 #define DECODE_HITACHI_AC344 _IR_ENABLE_DEFAULT_ #endif // DECODE_HITACHI_AC344 @@ -587,6 +616,13 @@ #define SEND_TECO _IR_ENABLE_DEFAULT_ #endif // SEND_TECO +#ifndef DECODE_TCL96AC +#define DECODE_TCL96AC _IR_ENABLE_DEFAULT_ +#endif // DECODE_TCL96AC +#ifndef SEND_TCL96AC +#define SEND_TCL96AC _IR_ENABLE_DEFAULT_ +#endif // SEND_TCL96AC + #ifndef DECODE_TCL112AC #define DECODE_TCL112AC _IR_ENABLE_DEFAULT_ #endif // DECODE_TCL112AC @@ -839,6 +875,55 @@ #define SEND_AIRTON _IR_ENABLE_DEFAULT_ #endif // SEND_AIRTON +#ifndef DECODE_KELON168 +#define DECODE_KELON168 _IR_ENABLE_DEFAULT_ +#endif // DECODE_KELON168 +#ifndef SEND_KELON168 +#define SEND_KELON168 _IR_ENABLE_DEFAULT_ +#endif // SEND_KELON168 + +#ifndef DECODE_DAIKIN200 +#define DECODE_DAIKIN200 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN200 +#ifndef SEND_DAIKIN200 +#define SEND_DAIKIN200 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN200 + +#ifndef DECODE_HAIER_AC160 +#define DECODE_HAIER_AC160 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HAIER_AC160 +#ifndef SEND_HAIER_AC160 +#define SEND_HAIER_AC160 _IR_ENABLE_DEFAULT_ +#endif // SEND_HAIER_AC160 + +#ifndef DECODE_TOTO +#define DECODE_TOTO _IR_ENABLE_DEFAULT_ +#endif // DECODE_TOTO +#ifndef SEND_TOTO +#define SEND_TOTO _IR_ENABLE_DEFAULT_ +#endif // SEND_TOTO + +#ifndef DECODE_CLIMABUTLER +#define DECODE_CLIMABUTLER _IR_ENABLE_DEFAULT_ +#endif // DECODE_CLIMABUTLER +#ifndef SEND_CLIMABUTLER +#define SEND_CLIMABUTLER _IR_ENABLE_DEFAULT_ +#endif // SEND_CLIMABUTLER + +#ifndef DECODE_BOSCH144 +#define DECODE_BOSCH144 _IR_ENABLE_DEFAULT_ +#endif // DECODE_BOSCH144 +#ifndef SEND_BOSCH144 +#define SEND_BOSCH144 _IR_ENABLE_DEFAULT_ +#endif // SEND_BOSCH144 + +#ifndef DECODE_DAIKIN312 +#define DECODE_DAIKIN312 _IR_ENABLE_DEFAULT_ +#endif // DECODE_DAIKIN312 +#ifndef SEND_DAIKIN312 +#define SEND_DAIKIN312 _IR_ENABLE_DEFAULT_ +#endif // SEND_DAIKIN312 + #if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ @@ -853,7 +938,10 @@ DECODE_HITACHI_AC344 || DECODE_CORONA_AC || DECODE_SANYO_AC || \ DECODE_VOLTAS || DECODE_MIRAGE || DECODE_HAIER_AC176 || \ DECODE_TEKNOPOINT || DECODE_KELON || DECODE_TROTEC_3550 || \ - DECODE_SANYO_AC88 || DECODE_RHOSS || \ + DECODE_SANYO_AC88 || DECODE_RHOSS || DECODE_HITACHI_AC264 || \ + DECODE_KELON168 || DECODE_HITACHI_AC296 || DECODE_CARRIER_AC128 || \ + DECODE_DAIKIN200 || DECODE_HAIER_AC160 || DECODE_TCL96AC || \ + DECODE_BOSCH144 || DECODE_SANYO_AC152 || DECODE_DAIKIN312 || \ false) // Add any DECODE to the above if it uses result->state (see kStateSizeMax) // you might also want to add the protocol to hasACState function @@ -1004,8 +1092,20 @@ enum decode_type_t { RHOSS, AIRTON, COOLIX48, // 110 + HITACHI_AC264, + KELON168, + HITACHI_AC296, + DAIKIN200, + HAIER_AC160, // 115 + CARRIER_AC128, + TOTO, + CLIMABUTLER, + TCL96AC, + BOSCH144, // 120 + SANYO_AC152, + DAIKIN312, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = COOLIX48, + kLastDecodeType = DAIKIN312, }; // Message lengths & required repeat values @@ -1026,6 +1126,8 @@ const uint16_t kArgoStateLength = 12; const uint16_t kArgoBits = kArgoStateLength * 8; const uint16_t kArgoDefaultRepeat = kNoRepeat; const uint16_t kArrisBits = 32; +const uint16_t kBosch144StateLength = 18; +const uint16_t kBosch144Bits = kBosch144StateLength * 8; const uint16_t kCoolixBits = 24; const uint16_t kCoolix48Bits = kCoolixBits * 2; const uint16_t kCoolixDefaultRepeat = kSingleRepeat; @@ -1035,6 +1137,9 @@ const uint16_t kCarrierAc40Bits = 40; const uint16_t kCarrierAc40MinRepeat = 2; const uint16_t kCarrierAc64Bits = 64; const uint16_t kCarrierAc64MinRepeat = kNoRepeat; +const uint16_t kCarrierAc128StateLength = 16; +const uint16_t kCarrierAc128Bits = kCarrierAc128StateLength * 8; +const uint16_t kCarrierAc128MinRepeat = kNoRepeat; const uint16_t kCoronaAcStateLengthShort = 7; const uint16_t kCoronaAcStateLength = kCoronaAcStateLengthShort * 3; const uint16_t kCoronaAcBitsShort = kCoronaAcStateLengthShort * 8; @@ -1061,9 +1166,15 @@ const uint16_t kDaikin152DefaultRepeat = kNoRepeat; const uint16_t kDaikin176StateLength = 22; const uint16_t kDaikin176Bits = kDaikin176StateLength * 8; const uint16_t kDaikin176DefaultRepeat = kNoRepeat; +const uint16_t kDaikin200StateLength = 25; +const uint16_t kDaikin200Bits = kDaikin200StateLength * 8; +const uint16_t kDaikin200DefaultRepeat = kNoRepeat; const uint16_t kDaikin216StateLength = 27; const uint16_t kDaikin216Bits = kDaikin216StateLength * 8; const uint16_t kDaikin216DefaultRepeat = kNoRepeat; +const uint16_t kDaikin312StateLength = 39; +const uint16_t kDaikin312Bits = kDaikin312StateLength * 8; +const uint16_t kDaikin312DefaultRepeat = kNoRepeat; const uint16_t kDelonghiAcBits = 64; const uint16_t kDelonghiAcDefaultRepeat = kNoRepeat; const uint16_t kTechnibelAcBits = 56; @@ -1101,6 +1212,9 @@ const uint16_t kHaierAcDefaultRepeat = kNoRepeat; const uint16_t kHaierACYRW02StateLength = 14; const uint16_t kHaierACYRW02Bits = kHaierACYRW02StateLength * 8; const uint16_t kHaierAcYrw02DefaultRepeat = kNoRepeat; +const uint16_t kHaierAC160StateLength = 20; +const uint16_t kHaierAC160Bits = kHaierAC160StateLength * 8; +const uint16_t kHaierAc160DefaultRepeat = kNoRepeat; const uint16_t kHaierAC176StateLength = 22; const uint16_t kHaierAC176Bits = kHaierAC176StateLength * 8; const uint16_t kHaierAc176DefaultRepeat = kNoRepeat; @@ -1115,6 +1229,10 @@ const uint16_t kHitachiAc3StateLength = 27; const uint16_t kHitachiAc3Bits = kHitachiAc3StateLength * 8; const uint16_t kHitachiAc3MinStateLength = 15; const uint16_t kHitachiAc3MinBits = kHitachiAc3MinStateLength * 8; +const uint16_t kHitachiAc264StateLength = 33; +const uint16_t kHitachiAc264Bits = kHitachiAc264StateLength * 8; +const uint16_t kHitachiAc296StateLength = 37; +const uint16_t kHitachiAc296Bits = kHitachiAc296StateLength * 8; const uint16_t kHitachiAc344StateLength = 43; const uint16_t kHitachiAc344Bits = kHitachiAc344StateLength * 8; const uint16_t kHitachiAc424StateLength = 53; @@ -1123,6 +1241,8 @@ const uint16_t kInaxBits = 24; const uint16_t kInaxMinRepeat = kSingleRepeat; const uint16_t kJvcBits = 16; const uint16_t kKelonBits = 48; +const uint16_t kKelon168StateLength = 21; +const uint16_t kKelon168Bits = kKelon168StateLength * 8; const uint16_t kKelvinatorStateLength = 16; const uint16_t kKelvinatorBits = kKelvinatorStateLength * 8; const uint16_t kKelvinatorDefaultRepeat = kNoRepeat; @@ -1198,6 +1318,9 @@ const uint16_t kSanyoAcBits = kSanyoAcStateLength * 8; const uint16_t kSanyoAc88StateLength = 11; const uint16_t kSanyoAc88Bits = kSanyoAc88StateLength * 8; const uint16_t kSanyoAc88MinRepeat = 2; +const uint16_t kSanyoAc152StateLength = 19; +const uint16_t kSanyoAc152Bits = kSanyoAc152StateLength * 8; +const uint16_t kSanyoAc152MinRepeat = kNoRepeat; const uint16_t kSanyoSA8650BBits = 12; const uint16_t kSanyoLC7461AddressBits = 13; const uint16_t kSanyoLC7461CommandBits = 8; @@ -1218,6 +1341,9 @@ const uint16_t kSonyMinBits = 12; const uint16_t kSonyMinRepeat = 2; const uint16_t kSymphonyBits = 12; const uint16_t kSymphonyDefaultRepeat = 3; +const uint16_t kTcl96AcStateLength = 12; +const uint16_t kTcl96AcBits = kTcl96AcStateLength * 8; +const uint16_t kTcl96AcDefaultRepeat = kNoRepeat; const uint16_t kTcl112AcStateLength = 14; const uint16_t kTcl112AcBits = kTcl112AcStateLength * 8; const uint16_t kTcl112AcDefaultRepeat = kNoRepeat; @@ -1232,6 +1358,10 @@ const uint16_t kToshibaACStateLengthShort = kToshibaACStateLength - 2; const uint16_t kToshibaACBitsShort = kToshibaACStateLengthShort * 8; const uint16_t kToshibaACStateLengthLong = kToshibaACStateLength + 1; const uint16_t kToshibaACBitsLong = kToshibaACStateLengthLong * 8; +const uint16_t kTotoBits = 24; +const uint16_t kTotoShortBits = kTotoBits; +const uint16_t kTotoLongBits = kTotoShortBits * 2; +const uint16_t kTotoDefaultRepeat = kSingleRepeat; const uint16_t kTranscoldBits = 24; const uint16_t kTranscoldDefaultRepeat = kNoRepeat; const uint16_t kTrotecStateLength = 9; @@ -1255,6 +1385,7 @@ const uint16_t kBoseBits = 16; const uint16_t kRhossStateLength = 12; const uint16_t kRhossBits = kRhossStateLength * 8; const uint16_t kRhossDefaultRepeat = 0; +const uint16_t kClimaButlerBits = 52; // Legacy defines. (Deprecated) @@ -1282,6 +1413,8 @@ const uint16_t kRhossDefaultRepeat = 0; #define HITACHI_AC1_BITS kHitachiAc1Bits #define HITACHI_AC2_STATE_LENGTH kHitachiAc2StateLength #define HITACHI_AC2_BITS kHitachiAc2Bits +#define HITACHI_AC296_STATE_LENGTH kHitachiAc296StateLength +#define HITACHI_AC296_BITS kHitachiAc296Bits #define JVC_BITS kJvcBits #define KELVINATOR_STATE_LENGTH kKelvinatorStateLength #define LASERTAG_BITS kLasertagBits diff --git a/lib/IRremoteESP8266/src/IRsend.cpp b/lib/IRremoteESP8266/src/IRsend.cpp index e0a66e068..1fb1339c5 100644 --- a/lib/IRremoteESP8266/src/IRsend.cpp +++ b/lib/IRremoteESP8266/src/IRsend.cpp @@ -571,6 +571,7 @@ uint16_t IRsend::minRepeats(const decode_type_t protocol) { case MULTIBRACKETS: case SHERWOOD: case TOSHIBA_AC: + case TOTO: return kSingleRepeat; // Special case AIRWELL: @@ -633,6 +634,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { case MIDEA24: case NIKAI: case RCMM: + case TOTO: case TRANSCOLD: return 24; case LG: @@ -668,6 +670,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { case MIDEA: case PANASONIC: return 48; + case CLIMABUTLER: + return kClimaButlerBits; // 52 case AIRTON: case ECOCLIM: case MAGIQUEST: @@ -682,8 +686,12 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return 64; case ARGO: return kArgoBits; + case BOSCH144: + return kBosch144Bits; case CORONA_AC: return kCoronaAcBits; + case CARRIER_AC128: + return kCarrierAc128Bits; case DAIKIN: return kDaikinBits; case DAIKIN128: @@ -696,8 +704,12 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return kDaikin176Bits; case DAIKIN2: return kDaikin2Bits; + case DAIKIN200: + return kDaikin200Bits; case DAIKIN216: return kDaikin216Bits; + case DAIKIN312: + return kDaikin312Bits; case DAIKIN64: return kDaikin64Bits; case ELECTRA_AC: @@ -708,6 +720,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return kHaierACBits; case HAIER_AC_YRW02: return kHaierACYRW02Bits; + case HAIER_AC160: + return kHaierAC160Bits; case HAIER_AC176: return kHaierAC176Bits; case HITACHI_AC: @@ -718,10 +732,16 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return kHitachiAc2Bits; case HITACHI_AC3: return kHitachiAc3Bits; + case HITACHI_AC264: + return kHitachiAc264Bits; + case HITACHI_AC296: + return kHitachiAc296Bits; case HITACHI_AC344: return kHitachiAc344Bits; case HITACHI_AC424: return kHitachiAc424Bits; + case KELON168: + return kKelon168Bits; case KELVINATOR: return kKelvinatorBits; case MILESTAG2: @@ -750,8 +770,12 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return kSanyoAcBits; case SANYO_AC88: return kSanyoAc88Bits; + case SANYO_AC152: + return kSanyoAc152Bits; case SHARP_AC: return kSharpAcBits; + case TCL96AC: + return kTcl96AcBits; case TCL112AC: return kTcl112AcBits; case TEKNOPOINT: @@ -827,6 +851,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, sendCarrierAC64(data, nbits, min_repeat); break; #endif // SEND_CARRIER_AC64 +#if SEND_CLIMABUTLER + case CLIMABUTLER: + sendClimaButler(data, nbits, min_repeat); + break; +#endif // SEND_CLIMABUTLER #if SEND_COOLIX case COOLIX: sendCOOLIX(data, nbits, min_repeat); @@ -1060,6 +1089,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, sendTeco(data, nbits, min_repeat); break; #endif // SEND_TECO +#if SEND_TOTO + case TOTO: + sendToto(data, nbits, min_repeat); + break; +#endif // SEND_TOTO #if SEND_TRANSCOLD case TRANSCOLD: sendTranscold(data, nbits, min_repeat); @@ -1120,6 +1154,16 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state, sendArgo(state, nbytes); break; #endif // SEND_ARGO +#if SEND_BOSCH144 + case BOSCH144: + sendBosch144(state, nbytes); + break; +#endif // SEND_BOSCH144 +#if SEND_CARRIER_AC128 + case CARRIER_AC128: + sendCarrierAC128(state, nbytes); + break; +#endif // SEND_CARRIER_AC128 #if SEND_CORONA_AC case CORONA_AC: sendCoronaAc(state, nbytes); @@ -1155,11 +1199,21 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state, sendDaikin2(state, nbytes); break; #endif // SEND_DAIKIN2 +#if SEND_DAIKIN200 + case DAIKIN200: + sendDaikin200(state, nbytes); + break; +#endif // SEND_DAIKIN200 #if SEND_DAIKIN216 case DAIKIN216: sendDaikin216(state, nbytes); break; #endif // SEND_DAIKIN216 +#if SEND_DAIKIN312 + case DAIKIN312: + sendDaikin312(state, nbytes); + break; +#endif // SEND_DAIKIN312 #if SEND_ELECTRA_AC case ELECTRA_AC: sendElectraAC(state, nbytes); @@ -1185,6 +1239,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state, sendHaierACYRW02(state, nbytes); break; #endif // SEND_HAIER_AC_YRW02 +#if SEND_HAIER_AC160 + case HAIER_AC160: + sendHaierAC160(state, nbytes); + break; +#endif // SEND_HAIER_AC160 #if SEND_HAIER_AC176 case HAIER_AC176: sendHaierAC176(state, nbytes); @@ -1210,6 +1269,16 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state, sendHitachiAc3(state, nbytes); break; #endif // SEND_HITACHI_AC3 +#if SEND_HITACHI_AC264 + case HITACHI_AC264: + sendHitachiAc264(state, nbytes); + break; +#endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + case HITACHI_AC296: + sendHitachiAc296(state, nbytes); + break; +#endif // SEND_HITACHI_AC296 #if SEND_HITACHI_AC344 case HITACHI_AC344: sendHitachiAc344(state, nbytes); @@ -1220,6 +1289,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state, sendHitachiAc424(state, nbytes); break; #endif // SEND_HITACHI_AC424 +#if SEND_KELON168 + case KELON168: + sendKelon168(state, nbytes); + break; +#endif // SEND_KELON168 #if SEND_KELVINATOR case KELVINATOR: sendKelvinator(state, nbytes); @@ -1288,11 +1362,21 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state, sendSanyoAc88(state, nbytes); break; #endif // SEND_SANYO_AC88 +#if SEND_SANYO_AC152 + case SANYO_AC152: + sendSanyoAc152(state, nbytes); + break; +#endif // SEND_SANYO_AC152 #if SEND_SHARP_AC case SHARP_AC: sendSharpAc(state, nbytes); break; #endif // SEND_SHARP_AC +#if SEND_TCL96AC + case TCL96AC: + sendTcl96Ac(state, nbytes); + break; +#endif // SEND_TCL96AC #if SEND_TCL112AC case TCL112AC: sendTcl112Ac(state, nbytes); diff --git a/lib/IRremoteESP8266/src/IRsend.h b/lib/IRremoteESP8266/src/IRsend.h index 3696eb40f..02f2c939e 100644 --- a/lib/IRremoteESP8266/src/IRsend.h +++ b/lib/IRremoteESP8266/src/IRsend.h @@ -95,30 +95,31 @@ enum class swingh_t { /// Structure to hold a common A/C state. struct state_t { - 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; + decode_type_t protocol = decode_type_t::UNKNOWN; + int16_t model = -1; // `-1` means unused. + bool power = false; + stdAc::opmode_t mode = stdAc::opmode_t::kOff; + float degrees = 25; + bool celsius = true; + stdAc::fanspeed_t fanspeed = stdAc::fanspeed_t::kAuto; + stdAc::swingv_t swingv = stdAc::swingv_t::kOff; + stdAc::swingh_t swingh = stdAc::swingh_t::kOff; + bool quiet = false; + bool turbo = false; + bool econo = false; + bool light = false; + bool filter = false; + bool clean = false; + bool beep = false; + int16_t sleep = -1; // `-1` means off. + int16_t clock = -1; // `-1` means not set. }; }; // namespace stdAc /// Fujitsu A/C model numbers enum fujitsu_ac_remote_model_t { - ARRAH2E = 1, ///< (1) AR-RAH2E, AR-RAC1E, AR-RAE1E, AR-RCE1E (Default) + ARRAH2E = 1, ///< (1) AR-RAH2E, AR-RAC1E, AR-RAE1E, AR-RCE1E, AR-RAH2U, + ///< AR-REG1U (Default) ///< Warning: Use on incorrect models can cause the A/C to lock ///< up, requring the A/C to be physically powered off to fix. ///< e.g. AR-RAH1U may lock up with a Swing command. @@ -133,7 +134,9 @@ enum fujitsu_ac_remote_model_t { /// Gree A/C model numbers enum gree_ac_remote_model_t { YAW1F = 1, // (1) Ultimate, EKOKAI, RusClimate (Default) - YBOFB, // (2) Green, YBOFB2, YAPOF3 + YBOFB, // (2) Green, YBOFB2, YAPOF3 + YX1FSF, // (3) Soleus Air window unit (Similar to YAW1F, but with an + // Operation mode of Energy Saver (Econo)) }; /// HAIER_AC176 A/C model numbers @@ -172,10 +175,10 @@ enum sharp_ac_remote_model_t { A903 = 3, // 820 too }; -/// TCL A/C model numbers +/// TCL (& Teknopoint) A/C model numbers enum tcl_ac_remote_model_t { TAC09CHSD = 1, - GZ055BE1 = 2, + GZ055BE1 = 2, // Also Teknopoint GZ01-BEJ0-000 }; /// Voltas A/C model numbers @@ -196,6 +199,7 @@ enum lg_ac_remote_model_t { AKB75215403, // (2) LG2 28-bit Protocol AKB74955603, // (3) LG2 28-bit Protocol variant AKB73757604, // (4) LG2 Variant of AKB74955603 + LG6711A20083V, // (5) Same as GE6711AR2853M, but only SwingV toggle. }; @@ -342,6 +346,11 @@ class IRsend { const uint16_t nbytes = kSanyoAc88StateLength, const uint16_t repeat = kSanyoAc88MinRepeat); #endif // SEND_SANYO_AC88 +#if SEND_SANYO_AC152 + void sendSanyoAc152(const uint8_t *data, + const uint16_t nbytes = kSanyoAc152StateLength, + const uint16_t repeat = kSanyoAc152MinRepeat); +#endif // SEND_SANYO_AC152 #if SEND_DISH // sendDISH() should typically be called with repeat=3 as DISH devices // expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes) @@ -480,11 +489,21 @@ class IRsend { const uint16_t nbytes = kDaikin2StateLength, const uint16_t repeat = kDaikin2DefaultRepeat); #endif +#if SEND_DAIKIN200 + void sendDaikin200(const unsigned char data[], + const uint16_t nbytes = kDaikin200StateLength, + const uint16_t repeat = kDaikin200DefaultRepeat); +#endif // SEND_DAIKIN200 #if SEND_DAIKIN216 void sendDaikin216(const unsigned char data[], const uint16_t nbytes = kDaikin216StateLength, const uint16_t repeat = kDaikin216DefaultRepeat); -#endif +#endif // SEND_DAIKIN216 +#if SEND_DAIKIN312 + void sendDaikin312(const unsigned char data[], + const uint16_t nbytes = kDaikin312StateLength, + const uint16_t repeat = kDaikin312DefaultRepeat); +#endif // SEND_DAIKIN312 #if SEND_AIWA_RC_T501 void sendAiwaRCT501(uint64_t data, uint16_t nbits = kAiwaRcT501Bits, uint16_t repeat = kAiwaRcT501MinRepeats); @@ -556,6 +575,11 @@ class IRsend { void sendCarrierAC64(uint64_t data, uint16_t nbits = kCarrierAc64Bits, uint16_t repeat = kCarrierAc64MinRepeat); #endif +#if SEND_CARRIER_AC128 + void sendCarrierAC128(const uint8_t data[], + uint16_t nbytes = kCarrierAc128StateLength, + uint16_t repeat = kCarrierAc128MinRepeat); +#endif // SEND_CARRIER_AC128 #if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) void sendHaierAC(const unsigned char data[], const uint16_t nbytes = kHaierACStateLength, @@ -566,6 +590,11 @@ class IRsend { const uint16_t nbytes = kHaierACYRW02StateLength, const uint16_t repeat = kHaierAcYrw02DefaultRepeat); #endif // SEND_HAIER_AC_YRW02 +#if SEND_HAIER_AC160 + void sendHaierAC160(const unsigned char data[], + const uint16_t nbytes = kHaierAC160StateLength, + const uint16_t repeat = kHaierAc160DefaultRepeat); +#endif // SEND_HAIER_AC160 #if SEND_HAIER_AC176 void sendHaierAC176(const unsigned char data[], const uint16_t nbytes = kHaierAC176StateLength, @@ -592,6 +621,16 @@ class IRsend { // different sizes const uint16_t repeat = kHitachiAcDefaultRepeat); #endif // SEND_HITACHI_AC3 +#if SEND_HITACHI_AC264 + void sendHitachiAc264(const unsigned char data[], + const uint16_t nbytes = kHitachiAc264StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + void sendHitachiAc296(const unsigned char data[], + const uint16_t nbytes = kHitachiAc296StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC296 #if SEND_HITACHI_AC344 void sendHitachiAc344(const unsigned char data[], const uint16_t nbytes = kHitachiAc344StateLength, @@ -643,11 +682,16 @@ class IRsend { void sendVestelAc(const uint64_t data, const uint16_t nbits = kVestelAcBits, const uint16_t repeat = kNoRepeat); #endif +#if SEND_TCL96AC + void sendTcl96Ac(const unsigned char data[], + const uint16_t nbytes = kTcl96AcStateLength, + const uint16_t repeat = kTcl96AcDefaultRepeat); +#endif // SEND_TCL96AC #if SEND_TCL112AC void sendTcl112Ac(const unsigned char data[], const uint16_t nbytes = kTcl112AcStateLength, const uint16_t repeat = kTcl112AcDefaultRepeat); -#endif +#endif // SEND_TCL112AC #if SEND_TECO void sendTeco(const uint64_t data, const uint16_t nbits = kTecoBits, const uint16_t repeat = kNoRepeat); @@ -755,6 +799,11 @@ class IRsend { void sendKelon(const uint64_t data, const uint16_t nbits = kKelonBits, const uint16_t repeat = kNoRepeat); #endif // SEND_KELON +#if SEND_KELON168 + void sendKelon168(const unsigned char data[], + const uint16_t nbytes = kKelon168StateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_KELON168 #if SEND_BOSE void sendBose(const uint64_t data, const uint16_t nbits = kBoseBits, const uint16_t repeat = kNoRepeat); @@ -774,6 +823,20 @@ class IRsend { void sendAirton(const uint64_t data, const uint16_t nbits = kAirtonBits, const uint16_t repeat = kAirtonDefaultRepeat); #endif // SEND_AIRTON +#if SEND_TOTO + void sendToto(const uint64_t data, const uint16_t nbits = kTotoBits, + const uint16_t repeat = kTotoDefaultRepeat); +#endif // SEND_TOTO +#if SEND_CLIMABUTLER + void sendClimaButler(const uint64_t data, + const uint16_t nbits = kClimaButlerBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_CLIMABUTLER +#if SEND_BOSCH144 + void sendBosch144(const unsigned char data[], + const uint16_t nbytes = kBosch144StateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_BOSCH144 protected: #ifdef UNIT_TEST diff --git a/lib/IRremoteESP8266/src/IRtext.cpp b/lib/IRremoteESP8266/src/IRtext.cpp index 077e7b0e7..405ad0aea 100644 --- a/lib/IRremoteESP8266/src/IRtext.cpp +++ b/lib/IRremoteESP8266/src/IRtext.cpp @@ -236,6 +236,7 @@ IRTEXT_CONST_STRING(kBitsStr, D_STR_BITS); ///< "Bits" // Model Names IRTEXT_CONST_STRING(kYaw1fStr, D_STR_YAW1F); ///< "YAW1F" IRTEXT_CONST_STRING(kYbofbStr, D_STR_YBOFB); ///< "YBOFB" +IRTEXT_CONST_STRING(kYx1fsfStr, D_STR_YX1FSF); ///< "YX1FSF" IRTEXT_CONST_STRING(kV9014557AStr, D_STR_V9014557_A); ///< "V9014557-A" IRTEXT_CONST_STRING(kV9014557BStr, D_STR_V9014557_B); ///< "V9014557-B" IRTEXT_CONST_STRING(kRlt0541htaaStr, D_STR_RLT0541HTA_A); ///< "R-LT0541-HTA-A" @@ -251,6 +252,8 @@ IRTEXT_CONST_STRING(kGe6711ar2853mStr, D_STR_GE6711AR2853M); ///< IRTEXT_CONST_STRING(kAkb75215403Str, D_STR_AKB75215403); ///< "AKB75215403" IRTEXT_CONST_STRING(kAkb74955603Str, D_STR_AKB74955603); ///< "AKB74955603" IRTEXT_CONST_STRING(kAkb73757604Str, D_STR_AKB73757604); ///< "AKB73757604" +IRTEXT_CONST_STRING(kLg6711a20083vStr, D_STR_LG6711A20083V); ///< + ///< "LG6711A20083V" IRTEXT_CONST_STRING(kKkg9ac1Str, D_STR_KKG9AC1); ///< "KKG9AC1" IRTEXT_CONST_STRING(kKkg29ac1Str, D_STR_KKG29AC1); ///< "KKG29AC1" IRTEXT_CONST_STRING(kLkeStr, D_STR_LKE); ///< "LKE" @@ -277,120 +280,621 @@ IRTEXT_CONST_STRING(kDg11j13aStr, D_STR_DG11J13A); ///< "DG11J13A" IRTEXT_CONST_STRING(kDg11j104Str, D_STR_DG11J104); ///< "DG11J104" IRTEXT_CONST_STRING(kDg11j191Str, D_STR_DG11J191); ///< "DG11J191" +#define D_STR_UNSUPPORTED "?" // Unsupported protocols will be showing as a question mark, check for length > 1 to show only currently included protocols // Protocol Names // Needs to be in decode_type_t order. IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { D_STR_UNUSED "\x0" + #if DECODE_RC5 || SEND_RC5 D_STR_RC5 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_RC6 || SEND_RC6 D_STR_RC6 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_NEC || SEND_NEC D_STR_NEC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_SONY || SEND_SONY D_STR_SONY "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_PANASONIC || SEND_PANASONIC D_STR_PANASONIC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_JVC || SEND_JVC D_STR_JVC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_SAMSUNG || SEND_SAMSUNG D_STR_SAMSUNG "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_WHYNTER || SEND_WHYNTER D_STR_WHYNTER "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_AIWA_RC_T501 || SEND_AIWA_RC_T501 D_STR_AIWA_RC_T501 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_LG || SEND_LG D_STR_LG "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_SANYO || SEND_SANYO D_STR_SANYO "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MITSUBISHI || SEND_MITSUBISHI D_STR_MITSUBISHI "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DISH || SEND_DISH D_STR_DISH "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_SHARP || SEND_SHARP D_STR_SHARP "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_COOLIX || SEND_COOLIX D_STR_COOLIX "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DAIKIN || SEND_DAIKIN D_STR_DAIKIN "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DENON || SEND_DENON D_STR_DENON "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_KELVINATOR || SEND_KELVINATOR D_STR_KELVINATOR "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if SEND_SHERWOOD // SEND-ONLY D_STR_SHERWOOD "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MITSUBISHI_AC || SEND_MITSUBISHI_AC D_STR_MITSUBISHI_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_RCMM || SEND_RCMM D_STR_RCMM "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_SANYO_LC7461 || SEND_SANYO_LC7461 D_STR_SANYO_LC7461 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_RC5X || SEND_RC5X D_STR_RC5X "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_GREE || SEND_GREE D_STR_GREE "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if SEND_PRONTO // SEND-ONLY D_STR_PRONTO "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_NEC_LIKE || SEND_NEC_LIKE D_STR_NEC_LIKE "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_ARGO || SEND_ARGO D_STR_ARGO "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_TROTEC || SEND_TROTEC D_STR_TROTEC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_NIKAI || SEND_NIKAI D_STR_NIKAI "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if SEND_RAW // SEND-ONLY D_STR_RAW "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if SEND_GLOBALCACHE // SEND-ONLY D_STR_GLOBALCACHE "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_TOSHIBA_AC || SEND_TOSHIBA_AC D_STR_TOSHIBA_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_FUJITSU_AC || SEND_FUJITSU_AC D_STR_FUJITSU_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MIDEA || SEND_MIDEA D_STR_MIDEA "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MAGIQUEST || SEND_MAGIQUEST D_STR_MAGIQUEST "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_LASERTAG || SEND_LASERTAG D_STR_LASERTAG "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_CARRIER_AC || SEND_CARRIER_AC D_STR_CARRIER_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_HAIER_AC || SEND_HAIER_AC D_STR_HAIER_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MITSUBISHI2 || SEND_MITSUBISHI2 D_STR_MITSUBISHI2 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_HITACHI_AC || SEND_HITACHI_AC D_STR_HITACHI_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_HITACHI_AC1 || SEND_HITACHI_AC1 D_STR_HITACHI_AC1 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_HITACHI_AC2 || SEND_HITACHI_AC2 D_STR_HITACHI_AC2 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_GICABLE || SEND_GICABLE D_STR_GICABLE "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_HAIER_AC_YRW02 || SEND_HAIER_AC_YRW02 D_STR_HAIER_AC_YRW02 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_WHIRLPOOL_AC || SEND_WHIRLPOOL_AC D_STR_WHIRLPOOL_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_SAMSUNG_AC || SEND_SAMSUNG_AC D_STR_SAMSUNG_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_LUTRON || SEND_LUTRON D_STR_LUTRON "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_ELECTRA_AC || SEND_ELECTRA_AC D_STR_ELECTRA_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_PANASONIC_AC || SEND_PANASONIC_AC D_STR_PANASONIC_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_PIONEER || SEND_PIONEER D_STR_PIONEER "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_LG2 || SEND_LG2 D_STR_LG2 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MWM || SEND_MWM D_STR_MWM "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DAIKIN2 || SEND_DAIKIN2 D_STR_DAIKIN2 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_VESTEL_AC || SEND_VESTEL_AC D_STR_VESTEL_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_TECO || SEND_TECO D_STR_TECO "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_SAMSUNG36 || SEND_SAMSUNG36 D_STR_SAMSUNG36 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_TCL112AC || SEND_TCL112AC D_STR_TCL112AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_LEGOPF || SEND_LEGO D_STR_LEGOPF "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MITSUBISHI_HEAVY || SEND_MITSUBISHI_HEAVY // Exception D_STR_MITSUBISHI_HEAVY_88 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MITSUBISHI_HEAVY || SEND_MITSUBISHI_HEAVY // Exception D_STR_MITSUBISHI_HEAVY_152 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DAIKIN216 || SEND_DAIKIN216 D_STR_DAIKIN216 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_SHARP_AC || SEND_SHARP_AC D_STR_SHARP_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_GOODWEATHER || SEND_GOODWEATHER D_STR_GOODWEATHER "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_INAX || SEND_INAX D_STR_INAX "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DAIKIN160 || SEND_DAIKIN160 D_STR_DAIKIN160 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_NEOCLIMA || SEND_NEOCLIMA D_STR_NEOCLIMA "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DAIKIN176 || SEND_DAIKIN176 D_STR_DAIKIN176 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DAIKIN128 || SEND_DAIKIN128 D_STR_DAIKIN128 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_AMCOR || SEND_AMCOR D_STR_AMCOR "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DAIKIN152 || SEND_DAIKIN152 D_STR_DAIKIN152 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MITSUBISHI136 || SEND_MITSUBISHI136 D_STR_MITSUBISHI136 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MITSUBISHI112 || SEND_MITSUBISHI112 D_STR_MITSUBISHI112 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_HITACHI_AC424 || SEND_HITACHI_AC424 D_STR_HITACHI_AC424 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if SEND_SONY // Exception D_STR_SONY_38K "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_EPSON || SEND_EPSON D_STR_EPSON "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_SYMPHONY || SEND_SYMPHONY D_STR_SYMPHONY "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_HITACHI_AC3 || SEND_HITACHI_AC3 D_STR_HITACHI_AC3 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DAIKIN64 || SEND_DAIKIN64 D_STR_DAIKIN64 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_AIRWELL || SEND_AIRWELL D_STR_AIRWELL "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DELONGHI_AC || SEND_DELONGHI_AC D_STR_DELONGHI_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DOSHISHA || SEND_DOSHISHA D_STR_DOSHISHA "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MULTIBRACKETS || SEND_MULTIBRACKETS D_STR_MULTIBRACKETS "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_CARRIER_AC40 || SEND_CARRIER_AC40 D_STR_CARRIER_AC40 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_CARRIER_AC64 || SEND_CARRIER_AC64 D_STR_CARRIER_AC64 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_HITACHI_AC344 || SEND_HITACHI_AC344 D_STR_HITACHI_AC344 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_CORONA_AC || SEND_CORONA_AC D_STR_CORONA_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MIDEA24 || SEND_MIDEA24 D_STR_MIDEA24 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_ZEPEAL || SEND_ZEPEAL D_STR_ZEPEAL "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_SANYO_AC || SEND_SANYO_AC D_STR_SANYO_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_VOLTAS || SEND_VOLTAS D_STR_VOLTAS "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_METZ || SEND_METZ D_STR_METZ "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_TRANSCOLD || SEND_TRANSCOLD D_STR_TRANSCOLD "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_TECHNIBEL_AC || SEND_TECHNIBEL_AC D_STR_TECHNIBEL_AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MIRAGE || SEND_MIRAGE D_STR_MIRAGE "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_ELITESCREENS || SEND_ELITESCREENS D_STR_ELITESCREENS "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_PANASONIC_AC32 || SEND_PANASONIC_AC32 D_STR_PANASONIC_AC32 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_MILESTAG2 || SEND_MILESTAG2 D_STR_MILESTAG2 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_ECOCLIM || SEND_ECOCLIM D_STR_ECOCLIM "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_XMP || SEND_XMP D_STR_XMP "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_TRUMA || SEND_TRUMA D_STR_TRUMA "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_HAIER_AC176 || SEND_HAIER_AC176 D_STR_HAIER_AC176 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_TEKNOPOINT || SEND_TEKNOPOINT D_STR_TEKNOPOINT "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_KELON || SEND_KELON D_STR_KELON "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_TROTEC_3550 || SEND_TROTEC_3550 D_STR_TROTEC_3550 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_SANYO_AC88 || SEND_SANYO_AC88 D_STR_SANYO_AC88 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_BOSE || SEND_BOSE D_STR_BOSE "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_ARRIS || SEND_ARRIS D_STR_ARRIS "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_RHOSS || SEND_RHOSS D_STR_RHOSS "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_AIRTON || SEND_AIRTON D_STR_AIRTON "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_COOLIX48 || SEND_COOLIX48 D_STR_COOLIX48 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_HITACHI_AC264 || SEND_HITACHI_AC264 + D_STR_HITACHI_AC264 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_KELON168 || SEND_KELON168 + D_STR_KELON168 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_HITACHI_AC296 || SEND_HITACHI_AC296 + D_STR_HITACHI_AC296 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DAIKIN200 || SEND_DAIKIN200 + D_STR_DAIKIN200 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_HAIER_AC160 || SEND_HAIER_AC160 + D_STR_HAIER_AC160 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_CARRIER_AC128 || SEND_CARRIER_AC128 + D_STR_CARRIER_AC128 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_TOTO || SEND_TOTO + D_STR_TOTO "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_CLIMABUTLER || SEND_CLIMABUTLER + D_STR_CLIMABUTLER "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_TCL96AC || SEND_TCL96AC + D_STR_TCL96AC "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_BOSCH144 || SEND_BOSCH144 + D_STR_BOSCH144 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_SANYO_AC152 || SEND_SANYO_AC152 + D_STR_SANYO_AC152 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif + #if DECODE_DAIKIN312 || SEND_DAIKIN312 + D_STR_DAIKIN312 "\x0" + #else + D_STR_UNSUPPORTED "\x0" + #endif ///< New protocol strings should be added just above this line. "\x0" ///< This string requires double null termination. }; diff --git a/lib/IRremoteESP8266/src/IRtext.h b/lib/IRremoteESP8266/src/IRtext.h index 031f73879..c21233761 100644 --- a/lib/IRremoteESP8266/src/IRtext.h +++ b/lib/IRremoteESP8266/src/IRtext.h @@ -1,4 +1,4 @@ -// Copyright 2019-2021 - David Conran (@crankyoldgit) +// Copyright 2019-2022 - David Conran (@crankyoldgit) // This header file is to be included in files **other than** 'IRtext.cpp'. // // WARNING: Do not edit this file! This file is automatically generated by @@ -33,6 +33,7 @@ extern IRTEXT_CONST_PTR(k8CHeatStr); extern IRTEXT_CONST_PTR(kA705Str); extern IRTEXT_CONST_PTR(kA903Str); extern IRTEXT_CONST_PTR(kA907Str); +extern IRTEXT_CONST_PTR(kAbsenseDetectStr); extern IRTEXT_CONST_PTR(kAirFlowStr); extern IRTEXT_CONST_PTR(kAkb73757604Str); extern IRTEXT_CONST_PTR(kAkb74955603Str); @@ -74,6 +75,8 @@ extern IRTEXT_CONST_PTR(kDehumidifyStr); extern IRTEXT_CONST_PTR(kDg11j104Str); extern IRTEXT_CONST_PTR(kDg11j13aStr); extern IRTEXT_CONST_PTR(kDg11j191Str); +extern IRTEXT_CONST_PTR(kDirectIndirectModeStr); +extern IRTEXT_CONST_PTR(kDirectStr); extern IRTEXT_CONST_PTR(kDisplayTempStr); extern IRTEXT_CONST_PTR(kDkeStr); extern IRTEXT_CONST_PTR(kDownStr); @@ -108,8 +111,10 @@ extern IRTEXT_CONST_PTR(kHourStr); extern IRTEXT_CONST_PTR(kHumidStr); extern IRTEXT_CONST_PTR(kIdStr); extern IRTEXT_CONST_PTR(kIFeelStr); +extern IRTEXT_CONST_PTR(kIndirectStr); extern IRTEXT_CONST_PTR(kInsideStr); extern IRTEXT_CONST_PTR(kIonStr); +extern IRTEXT_CONST_PTR(kISeeStr); extern IRTEXT_CONST_PTR(kJkeStr); extern IRTEXT_CONST_PTR(kKkg29ac1Str); extern IRTEXT_CONST_PTR(kKkg9ac1Str); @@ -117,11 +122,12 @@ extern IRTEXT_CONST_PTR(kLastStr); extern IRTEXT_CONST_PTR(kLeftMaxNoSpaceStr); extern IRTEXT_CONST_PTR(kLeftMaxStr); extern IRTEXT_CONST_PTR(kLeftStr); +extern IRTEXT_CONST_PTR(kLg6711a20083vStr); extern IRTEXT_CONST_PTR(kLightStr); extern IRTEXT_CONST_PTR(kLightToggleStr); extern IRTEXT_CONST_PTR(kLkeStr); -extern IRTEXT_CONST_PTR(kLoStr); extern IRTEXT_CONST_PTR(kLockStr); +extern IRTEXT_CONST_PTR(kLoStr); extern IRTEXT_CONST_PTR(kLoudStr); extern IRTEXT_CONST_PTR(kLowerStr); extern IRTEXT_CONST_PTR(kLowestStr); @@ -229,13 +235,8 @@ extern IRTEXT_CONST_PTR(kXFanStr); extern IRTEXT_CONST_PTR(kYaw1fStr); extern IRTEXT_CONST_PTR(kYbofbStr); extern IRTEXT_CONST_PTR(kYesStr); +extern IRTEXT_CONST_PTR(kYx1fsfStr); extern IRTEXT_CONST_PTR(kZoneFollowStr); extern IRTEXT_CONST_PTR(kAllProtocolNamesStr); -extern IRTEXT_CONST_PTR(kISeeStr); -extern IRTEXT_CONST_PTR(kEcocoolStr); -extern IRTEXT_CONST_PTR(kAbsenseDetectStr); -extern IRTEXT_CONST_PTR(kDirectIndirectModeStr); -extern IRTEXT_CONST_PTR(kDirectStr); -extern IRTEXT_CONST_PTR(kIndirectStr); #endif // IRTEXT_H_ diff --git a/lib/IRremoteESP8266/src/IRutils.cpp b/lib/IRremoteESP8266/src/IRutils.cpp index e1f889966..7c59228c0 100644 --- a/lib/IRremoteESP8266/src/IRutils.cpp +++ b/lib/IRremoteESP8266/src/IRutils.cpp @@ -172,6 +172,8 @@ bool hasACState(const decode_type_t protocol) { // This is kept sorted by name case AMCOR: case ARGO: + case BOSCH144: + case CARRIER_AC128: case CORONA_AC: case DAIKIN: case DAIKIN128: @@ -179,19 +181,25 @@ bool hasACState(const decode_type_t protocol) { case DAIKIN160: case DAIKIN176: case DAIKIN2: + case DAIKIN200: case DAIKIN216: + case DAIKIN312: case ELECTRA_AC: case FUJITSU_AC: case GREE: case HAIER_AC: case HAIER_AC_YRW02: + case HAIER_AC160: case HAIER_AC176: case HITACHI_AC: case HITACHI_AC1: case HITACHI_AC2: case HITACHI_AC3: + case HITACHI_AC264: + case HITACHI_AC296: case HITACHI_AC344: case HITACHI_AC424: + case KELON168: case KELVINATOR: case MIRAGE: case MITSUBISHI136: @@ -206,7 +214,9 @@ bool hasACState(const decode_type_t protocol) { case SAMSUNG_AC: case SANYO_AC: case SANYO_AC88: + case SANYO_AC152: case SHARP_AC: + case TCL96AC: case TCL112AC: case TEKNOPOINT: case TOSHIBA_AC: @@ -602,9 +612,10 @@ namespace irutils { break; case decode_type_t::GREE: switch (model) { - case gree_ac_remote_model_t::YAW1F: return kYaw1fStr; - case gree_ac_remote_model_t::YBOFB: return kYbofbStr; - default: return kUnknownStr; + case gree_ac_remote_model_t::YAW1F: return kYaw1fStr; + case gree_ac_remote_model_t::YBOFB: return kYbofbStr; + case gree_ac_remote_model_t::YX1FSF: return kYx1fsfStr; + default: return kUnknownStr; } break; case decode_type_t::HAIER_AC176: @@ -634,6 +645,7 @@ namespace irutils { case lg_ac_remote_model_t::AKB75215403: return kAkb75215403Str; case lg_ac_remote_model_t::AKB74955603: return kAkb74955603Str; case lg_ac_remote_model_t::AKB73757604: return kAkb73757604Str; + case lg_ac_remote_model_t::LG6711A20083V: return kLg6711a20083vStr; default: return kUnknownStr; } break; diff --git a/lib/IRremoteESP8266/src/ir_Airton.cpp b/lib/IRremoteESP8266/src/ir_Airton.cpp index 83ad95ef7..b71214d60 100644 --- a/lib/IRremoteESP8266/src/ir_Airton.cpp +++ b/lib/IRremoteESP8266/src/ir_Airton.cpp @@ -317,7 +317,7 @@ bool IRAirtonAc::getHealth(void) const { return _.Health; } /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRAirtonAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::AIRTON; result.power = getPower(); result.mode = toCommonMode(getMode()); diff --git a/lib/IRremoteESP8266/src/ir_Airwell.cpp b/lib/IRremoteESP8266/src/ir_Airwell.cpp index 26053442e..2aa244a41 100644 --- a/lib/IRremoteESP8266/src/ir_Airwell.cpp +++ b/lib/IRremoteESP8266/src/ir_Airwell.cpp @@ -238,7 +238,7 @@ uint8_t IRAirwellAc::getTemp(void) const { /// @param[in] prev Ptr to the previous state if required. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRAirwellAc::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; + stdAc::state_t result{}; // Start with the previous state if given it. if (prev != NULL) { result = *prev; diff --git a/lib/IRremoteESP8266/src/ir_Amcor.cpp b/lib/IRremoteESP8266/src/ir_Amcor.cpp index c2aea5cdd..e2112c78e 100644 --- a/lib/IRremoteESP8266/src/ir_Amcor.cpp +++ b/lib/IRremoteESP8266/src/ir_Amcor.cpp @@ -314,7 +314,7 @@ stdAc::fanspeed_t IRAmcorAc::toCommonFanSpeed(const uint8_t speed) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRAmcorAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::AMCOR; result.power = getPower(); result.mode = toCommonMode(_.Mode); diff --git a/lib/IRremoteESP8266/src/ir_Argo.cpp b/lib/IRremoteESP8266/src/ir_Argo.cpp index e1df28efb..04cde67ed 100644 --- a/lib/IRremoteESP8266/src/ir_Argo.cpp +++ b/lib/IRremoteESP8266/src/ir_Argo.cpp @@ -344,7 +344,7 @@ stdAc::fanspeed_t IRArgoAC::toCommonFanSpeed(const uint8_t speed) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRArgoAC::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::ARGO; result.power = _.Power; result.mode = toCommonMode(_.Mode); diff --git a/lib/IRremoteESP8266/src/ir_Bosch.cpp b/lib/IRremoteESP8266/src/ir_Bosch.cpp new file mode 100644 index 000000000..f6e26eb52 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Bosch.cpp @@ -0,0 +1,112 @@ +// Copyright 2022 David Conran +/// @file +/// @brief Support for the Bosch A/C / heatpump protocol +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1787 + +// Supports: +// Brand: Bosch, Model: CL3000i-Set 26 E A/C +// Brand: Bosch, Model: RG10A(G2S)BGEF remote + +#include +#ifndef ARDUINO +#include +#endif +#include "IRrecv.h" +#include "IRsend.h" +#include "IRtext.h" +#include "IRutils.h" + +// Constants +const uint16_t kBoschHdrMark = 4366; +const uint16_t kBoschBitMark = 502; +const uint16_t kBoschHdrSpace = 4415; +const uint16_t kBoschOneSpace = 1645; +const uint16_t kBoschZeroSpace = 571; +const uint16_t kBoschFooterSpace = 5235; +const uint16_t kBoschFreq = 38000; // Hz. (Guessing the most common frequency.) +const uint16_t kBosch144NrOfSections = 3; + +#if SEND_BOSCH144 +/// Send a Bosch 144-bit / 18-byte message +/// Status: STABLE / Confirmed Working. +/// @param[in] data The message to be sent. +/// @param[in] nbytes The number of bytes of message to be sent. +/// @param[in] repeat The number of times the command is to be repeated. +void IRsend::sendBosch144(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + // nbytes is required to be a multiple of kBosch144NrOfSections. + if (nbytes % kBosch144NrOfSections != 0) return; + + // Set IR carrier frequency + enableIROut(kBoschFreq); + + for (uint16_t r = 0; r <= repeat; r++) { + const uint16_t kSectionByteSize = nbytes / kBosch144NrOfSections; + for (uint16_t offset = 0; offset < nbytes; offset += kSectionByteSize) + // Section Header + Data + Footer + sendGeneric(kBoschHdrMark, kBoschHdrSpace, + kBoschBitMark, kBoschOneSpace, + kBoschBitMark, kBoschZeroSpace, + kBoschBitMark, kBoschFooterSpace, + data + offset, kSectionByteSize, + kBoschFreq, true, 0, kDutyDefault); + space(kDefaultMessageGap); // Complete guess + } +} + +#endif // SEND_BOSCH144 + +#if DECODE_BOSCH144 +/// Decode the supplied Bosch 144-bit / 18-byte A/C message. +/// Status: STABLE / Confirmed Working. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// result. +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +bool IRrecv::decodeBosch144(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + + kBosch144NrOfSections * (kHeader + kFooter) - + 1 + offset) + return false; // Can't possibly be a valid BOSCH144 message. + if (strict && nbits != kBosch144Bits) + return false; // Not strictly a BOSCH144 message. + if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. + return false; + if (nbits % kBosch144NrOfSections != 0) + return false; // nbits has to be a multiple of kBosch144NrOfSections. + const uint16_t kSectionBits = nbits / kBosch144NrOfSections; + const uint16_t kSectionBytes = kSectionBits / 8; + const uint16_t kNBytes = kSectionBytes * kBosch144NrOfSections; + // Capture each section individually + for (uint16_t pos = 0, section = 0; + pos < kNBytes; + pos += kSectionBytes, section++) { + uint16_t used = 0; + // Section Header + Section Data + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, kSectionBits, + kBoschHdrMark, kBoschHdrSpace, + kBoschBitMark, kBoschOneSpace, + kBoschBitMark, kBoschZeroSpace, + kBoschBitMark, kBoschFooterSpace, + section >= kBosch144NrOfSections - 1, + _tolerance, kMarkExcess, true); + if (!used) return false; // Didn't match. + offset += used; + } + + // Compliance + + // Success + results->decode_type = decode_type_t::BOSCH144; + 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_BOSCH144 diff --git a/lib/IRremoteESP8266/src/ir_Carrier.cpp b/lib/IRremoteESP8266/src/ir_Carrier.cpp index 92fca4bd2..42b45f9ab 100644 --- a/lib/IRremoteESP8266/src/ir_Carrier.cpp +++ b/lib/IRremoteESP8266/src/ir_Carrier.cpp @@ -1,8 +1,9 @@ -// Copyright 2018, 2020 David Conran +// Copyright 2018-2022 David Conran /// @file /// @brief Carrier protocols. /// @see CarrierAc https://github.com/crankyoldgit/IRremoteESP8266/issues/385 /// @see CarrierAc64 https://github.com/crankyoldgit/IRremoteESP8266/issues/1127 +/// @see CarrierAc128 https://github.com/crankyoldgit/IRremoteESP8266/issues/1797 #include "ir_Carrier.h" #include @@ -45,6 +46,16 @@ const uint16_t kCarrierAc64OneSpace = 1736; const uint16_t kCarrierAc64ZeroSpace = 615; const uint32_t kCarrierAc64Gap = kDefaultMessageGap; // A guess. +const uint16_t kCarrierAc128HdrMark = 4600; +const uint16_t kCarrierAc128HdrSpace = 2600; +const uint16_t kCarrierAc128Hdr2Mark = 9300; +const uint16_t kCarrierAc128Hdr2Space = 5000; +const uint16_t kCarrierAc128BitMark = 340; +const uint16_t kCarrierAc128OneSpace = 1000; +const uint16_t kCarrierAc128ZeroSpace = 400; +const uint16_t kCarrierAc128SectionGap = 20600; +const uint16_t kCarrierAc128InterSpace = 6700; +const uint16_t kCarrierAc128SectionBits = kCarrierAc128Bits / 2; #if SEND_CARRIER_AC /// Send a Carrier HVAC formatted message. @@ -511,7 +522,7 @@ String IRCarrierAc64::toString(void) const { /// Convert the A/C state to it's common stdAc::state_t equivalent. /// @return A stdAc::state_t state. stdAc::state_t IRCarrierAc64::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::CARRIER_AC64; result.model = -1; // No models used. result.power = _.Power; @@ -533,3 +544,104 @@ stdAc::state_t IRCarrierAc64::toCommon(void) const { result.clock = -1; return result; } + +#if SEND_CARRIER_AC128 +/// Send a Carrier 128bit HVAC formatted message. +/// Status: BETA / Seems to work with tests. Needs testing agaisnt real devices. +/// @param[in] data The message to be sent. +/// @param[in] nbytes The byte size of the message being sent. +/// @param[in] repeat The number of times the message is to be repeated. +void IRsend::sendCarrierAC128(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + // Min length check. + if (nbytes <= kCarrierAc128StateLength / 2) return; + + enableIROut(kCarrierAcFreq); + // Handle repeats. + for (uint16_t r = 0; r <= repeat; r++) { + // First part of the message. + // Headers + Data + SectionGap + sendGeneric(kCarrierAc128HdrMark, kCarrierAc128HdrSpace, + kCarrierAc128BitMark, kCarrierAc128OneSpace, + kCarrierAc128BitMark, kCarrierAc128ZeroSpace, + kCarrierAc128BitMark, kCarrierAc128SectionGap, + data, nbytes / 2, kCarrierAcFreq, false, 0, kDutyDefault); + // Inter-message markers + mark(kCarrierAc128HdrMark); + space(kCarrierAc128InterSpace); + // Second part of the message + // Headers + Data + SectionGap + sendGeneric(kCarrierAc128Hdr2Mark, kCarrierAc128Hdr2Space, + kCarrierAc128BitMark, kCarrierAc128OneSpace, + kCarrierAc128BitMark, kCarrierAc128ZeroSpace, + kCarrierAc128BitMark, kCarrierAc128SectionGap, + data + (nbytes / 2), nbytes / 2, kCarrierAcFreq, + false, 0, kDutyDefault); + // Footer + mark(kCarrierAc128HdrMark); + space(kDefaultMessageGap); + } +} +#endif // SEND_CARRIER_AC128 + +#if DECODE_CARRIER_AC128 +/// Decode the supplied Carrier 128-bit HVAC message. +/// Status: STABLE / Expected to work. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// result. +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +bool IRrecv::decodeCarrierAC128(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (nbits + 2 * kHeader + kFooter) - 1 + offset) + return false; // Can't possibly be a valid Carrier message. + if (strict && nbits != kCarrierAc128Bits) + return false; // We expect Carrier to be 128 bits of message. + + uint16_t used; + uint16_t pos = 0; + const uint16_t sectionbits = nbits / 2; + // Match the first section. + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, sectionbits, + kCarrierAc128HdrMark, kCarrierAc128HdrSpace, + kCarrierAc128BitMark, kCarrierAc128OneSpace, + kCarrierAc128BitMark, kCarrierAc128ZeroSpace, + kCarrierAc128BitMark, kCarrierAc128SectionGap, true, + kUseDefTol, kMarkExcess, false); + if (used == 0) return false; // No match. + offset += used; + pos += sectionbits / 8; + // Look for the inter-message markers. + if (!matchMark(results->rawbuf[offset++], kCarrierAc128HdrMark)) + return false; + if (!matchSpace(results->rawbuf[offset++], kCarrierAc128InterSpace)) + return false; + // Now look for the second section. + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, sectionbits, + kCarrierAc128Hdr2Mark, kCarrierAc128Hdr2Space, + kCarrierAc128BitMark, kCarrierAc128OneSpace, + kCarrierAc128BitMark, kCarrierAc128ZeroSpace, + kCarrierAc128BitMark, kCarrierAc128SectionGap, true, + kUseDefTol, kMarkExcess, false); + if (used == 0) return false; // No match. + offset += used; + // Now check for the Footer. + if (!matchMark(results->rawbuf[offset++], kCarrierAc128HdrMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kDefaultMessageGap)) return false; + + + // Compliance + // if (strict && !IRCarrierAc128::validChecksum(results->value)) return false; + + // Success + results->bits = nbits; + results->decode_type = CARRIER_AC128; + return true; +} +#endif // DECODE_CARRIER_AC128 diff --git a/lib/IRremoteESP8266/src/ir_Carrier.h b/lib/IRremoteESP8266/src/ir_Carrier.h index aa9ea8447..c642c51e5 100644 --- a/lib/IRremoteESP8266/src/ir_Carrier.h +++ b/lib/IRremoteESP8266/src/ir_Carrier.h @@ -1,8 +1,9 @@ -// Copyright 2020 David Conran +// Copyright 2020-2022 David Conran /// @file /// @brief Carrier A/C /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1127 /// @see https://docs.google.com/spreadsheets/d/1EZy78L0cn1KDIX1aKq2biptejFqCjD5HO3tLiRvXf48/edit#gid=0 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1797 // Supports: // Brand: Carrier/Surrey, Model: 42QG5A55970 remote @@ -11,6 +12,7 @@ // Brand: Carrier/Surrey, Model: 619EGX0180E0 A/C // Brand: Carrier/Surrey, Model: 619EGX0220E0 A/C // Brand: Carrier/Surrey, Model: 53NGK009/012 Inverter +// Brand: Carrier, Model: 40GKX0E2006 remote (CARRIER_AC128) #ifndef IR_CARRIER_H_ #define IR_CARRIER_H_ diff --git a/lib/IRremoteESP8266/src/ir_ClimaButler.cpp b/lib/IRremoteESP8266/src/ir_ClimaButler.cpp new file mode 100644 index 000000000..d8b4f5a98 --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_ClimaButler.cpp @@ -0,0 +1,86 @@ +// Copyright 2022 benjy3gg +// Copyright 2022 David Conran (crankyoldgit) +/// @file +/// @brief Support for Clima-Butler protocol +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1812 + +// Supports: +// Brand: Clima-Butler, Model: AR-715 remote +// Brand: Clima-Butler, Model: RCS-SD43UWI A/C + +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + + +const uint16_t kClimaButlerBitMark = 511; // uSeconds +const uint16_t kClimaButlerHdrMark = kClimaButlerBitMark; +const uint16_t kClimaButlerHdrSpace = 3492; // uSeconds +const uint16_t kClimaButlerOneSpace = 1540; // uSeconds +const uint16_t kClimaButlerZeroSpace = 548; // uSeconds +const uint32_t kClimaButlerGap = kDefaultMessageGap; // uSeconds (A guess.) +const uint16_t kClimaButlerFreq = 38000; // Hz. (Guess.) + +#if SEND_CLIMABUTLER +/// Send a ClimaButler formatted message. +/// Status: STABLE / Confirmed working. +/// @param[in] data containing the IR command. +/// @param[in] nbits Nr. of bits to send. usually kClimaButlerBits +/// @param[in] repeat Nr. of times the message is to be repeated. +void IRsend::sendClimaButler(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + enableIROut(kClimaButlerFreq); + for (uint16_t r = 0; r <= repeat; r++) { + // Header + Data + sendGeneric(kClimaButlerHdrMark, kClimaButlerHdrSpace, + kClimaButlerBitMark, kClimaButlerOneSpace, + kClimaButlerBitMark, kClimaButlerZeroSpace, + kClimaButlerBitMark, kClimaButlerHdrSpace, + data, nbits, kClimaButlerFreq, true, 0, kDutyDefault); + // Footer + mark(kClimaButlerBitMark); + space(kClimaButlerGap); + } +} +#endif // SEND_CLIMABUTLER + +#if DECODE_CLIMABUTLER +/// Decode the supplied ClimaButler message. +/// Status: STABLE / Confirmed working. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +bool IRrecv::decodeClimaButler(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + 2 * kFooter - offset) + return false; // Too short a message to match. + if (strict && nbits != kClimaButlerBits) + return false; + + // Header + Data + uint16_t used = matchGeneric(results->rawbuf + offset, &(results->value), + results->rawlen - offset, nbits, + kClimaButlerHdrMark, kClimaButlerHdrSpace, + kClimaButlerBitMark, kClimaButlerOneSpace, + kClimaButlerBitMark, kClimaButlerZeroSpace, + kClimaButlerBitMark, kClimaButlerHdrSpace); + if (!used) return false; // Didn't matched. + offset += used; + // Footer + if (!matchMark(results->rawbuf[offset++], kClimaButlerBitMark)) + return false; + if (results->rawlen <= offset && !matchAtLeast(results->rawbuf[offset], + kClimaButlerGap)) + return false; + + // Success + results->decode_type = decode_type_t::CLIMABUTLER; + results->bits = nbits; + results->command = 0; + results->address = 0; + return true; +} +#endif // DECODE_CLIMABUTLER diff --git a/lib/IRremoteESP8266/src/ir_Coolix.cpp b/lib/IRremoteESP8266/src/ir_Coolix.cpp index 7b5b140f0..755e89190 100644 --- a/lib/IRremoteESP8266/src/ir_Coolix.cpp +++ b/lib/IRremoteESP8266/src/ir_Coolix.cpp @@ -32,7 +32,7 @@ const uint16_t kCoolixHdrSpaceTicks = 16; const uint16_t kCoolixHdrSpace = kCoolixHdrSpaceTicks * kCoolixTick; // 4416us const uint16_t kCoolixMinGapTicks = kCoolixHdrMarkTicks + kCoolixZeroSpaceTicks; const uint16_t kCoolixMinGap = kCoolixMinGapTicks * kCoolixTick; // 5244us -const uint8_t kCoolix48ExtraTolerance = 5; // Percent +const uint8_t kCoolixExtraTolerance = 5; // Percent using irutils::addBoolToString; using irutils::addIntToString; @@ -496,7 +496,7 @@ stdAc::fanspeed_t IRCoolixAC::toCommonFanSpeed(const uint8_t speed) { /// @param[in] prev Ptr to the previous state if required. /// @return A stdAc::state_t state. stdAc::state_t IRCoolixAC::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; + stdAc::state_t result{}; // Start with the previous state if given it. if (prev != NULL) { result = *prev; @@ -648,41 +648,39 @@ bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t offset, return false; // We can't possibly capture a Coolix packet that big. // Header - if (!matchMark(results->rawbuf[offset], kCoolixHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kCoolixHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kCoolixHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kCoolixHdrSpaceTicks; + if (!matchMark(results->rawbuf[offset++], kCoolixHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kCoolixHdrSpace)) return false; // Data // Twice as many bits as there are normal plus inverted bits. - for (uint16_t i = 0; i < nbits * 2; i++, offset++) { - bool flip = (i / 8) % 2; - if (!matchMark(results->rawbuf[offset++], kCoolixBitMarkTicks * m_tick)) - return false; - if (matchSpace(results->rawbuf[offset], kCoolixOneSpaceTicks * s_tick)) { - if (flip) - inverted = (inverted << 1) | 1; - else - data = (data << 1) | 1; - } else if (matchSpace(results->rawbuf[offset], - kCoolixZeroSpaceTicks * s_tick)) { - if (flip) - inverted <<= 1; - else - data <<= 1; + for (uint16_t i = 0; i < nbits * 2; i += 8) { + const bool flip = (i / 8) % 2; + uint64_t result = 0; + // Read the next byte of data. + const uint16_t used = matchGeneric(results->rawbuf + offset, &result, + results->rawlen - offset, 8, + 0, 0, // No Header + kCoolixBitMark, kCoolixOneSpace, // Data + kCoolixBitMark, kCoolixZeroSpace, + 0, 0, // No Footer + false, + _tolerance + kCoolixExtraTolerance, + 0, true); + if (!used) return false; // Didn't match a bytes worth of data. + offset += used; + if (flip) { // The inverted byte. + inverted <<= 8; + inverted |= result; } else { - return false; + data <<= 8; + data |= result; } } // Footer - if (!matchMark(results->rawbuf[offset++], kCoolixBitMarkTicks * m_tick)) - return false; + if (!matchMark(results->rawbuf[offset++], kCoolixBitMark)) return false; if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kCoolixMinGapTicks * s_tick)) - return false; + !matchAtLeast(results->rawbuf[offset], kCoolixMinGap)) return false; // Compliance uint64_t orig = data; // Save a copy of the data. @@ -721,7 +719,7 @@ void IRsend::sendCoolix48(const uint64_t data, const uint16_t nbits, } #endif // SEND_COOLIX48 -#if DECODE_COOLIX +#if DECODE_COOLIX48 /// Decode the supplied Coolix 48-bit A/C message. /// Status: BETA / Probably Working. /// @param[in,out] results Ptr to the data to decode & where to store the decode @@ -744,7 +742,7 @@ bool IRrecv::decodeCoolix48(decode_results *results, uint16_t offset, kCoolixBitMark, kCoolixOneSpace, kCoolixBitMark, kCoolixZeroSpace, kCoolixBitMark, kCoolixMinGap, - true, _tolerance + kCoolix48ExtraTolerance, 0, true)) + true, _tolerance + kCoolixExtraTolerance, 0, true)) return false; // Success diff --git a/lib/IRremoteESP8266/src/ir_Corona.cpp b/lib/IRremoteESP8266/src/ir_Corona.cpp index ef40c241a..da0e3db2b 100644 --- a/lib/IRremoteESP8266/src/ir_Corona.cpp +++ b/lib/IRremoteESP8266/src/ir_Corona.cpp @@ -550,7 +550,7 @@ String IRCoronaAc::toString(void) const { /// Convert the A/C state to it's common stdAc::state_t equivalent. /// @return A stdAc::state_t state. stdAc::state_t IRCoronaAc::toCommon() const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::CORONA_AC; result.model = -1; // No models used. result.power = _.Power; diff --git a/lib/IRremoteESP8266/src/ir_Daikin.cpp b/lib/IRremoteESP8266/src/ir_Daikin.cpp index ea11daf78..f44cd8f13 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.cpp +++ b/lib/IRremoteESP8266/src/ir_Daikin.cpp @@ -1,6 +1,6 @@ // Copyright 2016 sillyfrog // Copyright 2017 sillyfrog, crankyoldgit -// Copyright 2018-2021 crankyoldgit +// Copyright 2018-2022 crankyoldgit // Copyright 2019 pasna (IRDaikin160 class / Daikin176 class) /// @file @@ -21,6 +21,7 @@ /// @see Daikin216 https://github.com/crankyoldgit/IRremoteESP8266/issues/689 /// @see Daikin216 https://github.com/danny-source/Arduino_DY_IRDaikin /// @see Daikin64 https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 +/// @see Daikin200 https://github.com/crankyoldgit/IRremoteESP8266/issues/1802 #include "ir_Daikin.h" #include @@ -529,7 +530,7 @@ stdAc::fanspeed_t IRDaikinESP::toCommonFanSpeed(const uint8_t speed) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRDaikinESP::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::DAIKIN; result.model = -1; // No models used. result.power = _.Power; @@ -1199,7 +1200,7 @@ stdAc::swingh_t IRDaikin2::toCommonSwingH(const uint8_t setting) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRDaikin2::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::DAIKIN2; result.model = -1; // No models used. result.power = getPower(); @@ -1621,7 +1622,7 @@ bool IRDaikin216::getPowerful(void) const { return _.Powerful; } /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRDaikin216::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::DAIKIN216; result.model = -1; // No models used. result.power = _.Power; @@ -1971,7 +1972,7 @@ stdAc::swingv_t IRDaikin160::toCommonSwingV(const uint8_t setting) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRDaikin160::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::DAIKIN160; result.model = -1; // No models used. result.power = _.Power; @@ -2362,7 +2363,7 @@ stdAc::fanspeed_t IRDaikin176::toCommonFanSpeed(const uint8_t speed) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRDaikin176::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::DAIKIN176; result.model = -1; // No models used. result.power = _.Power; @@ -2883,7 +2884,7 @@ String IRDaikin128::toString(void) const { /// @param[in] prev Ptr to a previous state. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRDaikin128::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; + stdAc::state_t result{}; if (prev != NULL) result = *prev; result.protocol = decode_type_t::DAIKIN128; result.model = -1; // No models used. @@ -3285,7 +3286,7 @@ bool IRDaikin152::getComfort(void) const { return _.Comfort; } /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRDaikin152::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::DAIKIN152; result.model = -1; // No models used. result.power = _.Power; @@ -3710,7 +3711,7 @@ String IRDaikin64::toString(void) const { /// @param[in] prev Ptr to a previous state. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRDaikin64::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; + stdAc::state_t result{}; if (prev != NULL) result = *prev; result.protocol = decode_type_t::DAIKIN64; result.model = -1; // No models used. @@ -3733,3 +3734,193 @@ stdAc::state_t IRDaikin64::toCommon(const stdAc::state_t *prev) const { result.light = false; return result; } + +#if SEND_DAIKIN200 +/// Send a Daikin200 (200-bit) A/C formatted message. +/// Status: BETA / Untested on a real device. +/// @param[in] data The message to be sent. +/// @param[in] nbytes The number of bytes of message to be sent. +/// @param[in] repeat The number of times the command is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1802 +void IRsend::sendDaikin200(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin200Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 + sendGeneric(kDaikin200HdrMark, kDaikin200HdrSpace, kDaikin200BitMark, + kDaikin200OneSpace, kDaikin200BitMark, kDaikin200ZeroSpace, + kDaikin200BitMark, kDaikin200Gap, data, + kDaikin200Section1Length, + kDaikin200Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin200HdrMark, kDaikin200HdrSpace, kDaikin200BitMark, + kDaikin200OneSpace, kDaikin200BitMark, kDaikin200ZeroSpace, + kDaikin200BitMark, kDaikin200Gap, + data + kDaikin200Section1Length, + nbytes - kDaikin200Section1Length, + kDaikin200Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN200 + +#if DECODE_DAIKIN200 +/// Decode the supplied Daikin 200-bit message. (DAIKIN200) +/// Status: STABLE / Known to be working. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// result. +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1802 +bool IRrecv::decodeDaikin200(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset) + return false; + + // Compliance + if (strict && nbits != kDaikin200Bits) return false; + + const uint8_t ksectionSize[kDaikin200Sections] = {kDaikin200Section1Length, + kDaikin200Section2Length}; + // Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin200Sections; section++) { + uint16_t used; + // Section Header + Section Data + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin200HdrMark, kDaikin200HdrSpace, + kDaikin200BitMark, kDaikin200OneSpace, + kDaikin200BitMark, kDaikin200ZeroSpace, + kDaikin200BitMark, kDaikin200Gap, + section >= kDaikin200Sections - 1, + kDaikinTolerance, 0, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + if (pos * 8 != kDaikin200Bits) return false; + // Validate the checksum. + if (!IRDaikin176::validChecksum(results->state, pos)) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN200; + 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_DAIKIN200 + +#if SEND_DAIKIN312 +/// Send a Daikin312 (312-bit / 39 byte) A/C formatted message. +/// Status: BETA / Untested on a real device. +/// @param[in] data The message to be sent. +/// @param[in] nbytes The number of bytes of message to be sent. +/// @param[in] repeat The number of times the command is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1829 +void IRsend::sendDaikin312(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kDaikin312Section1Length) + return; // Not enough bytes to send a partial message. + + for (uint16_t r = 0; r <= repeat; r++) { + // Send the header, 0b00000 + sendGeneric(0, 0, // No header for the header + kDaikin312BitMark, kDaikin312OneSpace, + kDaikin312BitMark, kDaikin312ZeroSpace, + kDaikin312BitMark, kDaikin312HdrGap, + (uint64_t)0b00000, kDaikinHeaderLength, + kDaikin2Freq, false, 0, kDutyDefault); + // Section #1 + sendGeneric(kDaikin312HdrMark, kDaikin312HdrSpace, kDaikin312BitMark, + kDaikin312OneSpace, kDaikin312BitMark, kDaikin312ZeroSpace, + kDaikin312BitMark, kDaikin312SectionGap, data, + kDaikin312Section1Length, + kDaikin2Freq, false, 0, kDutyDefault); + // Section #2 + sendGeneric(kDaikin312HdrMark, kDaikin312HdrSpace, kDaikin312BitMark, + kDaikin312OneSpace, kDaikin312BitMark, kDaikin312ZeroSpace, + kDaikin312BitMark, kDaikin312SectionGap, + data + kDaikin312Section1Length, + nbytes - kDaikin312Section1Length, + kDaikin2Freq, false, 0, kDutyDefault); + } +} +#endif // SEND_DAIKIN312 + +#if DECODE_DAIKIN312 +/// Decode the supplied Daikin 312-bit / 39-byte message. (DAIKIN312) +/// Status: STABLE / Confirmed working. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// result. +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1829 +bool IRrecv::decodeDaikin312(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + // Is there enough data to match successfully? + if (results->rawlen < 2 * (nbits + kDaikinHeaderLength + kHeader + kFooter) + + kFooter - 1 + offset) + return false; + + // Compliance + if (strict && nbits != kDaikin312Bits) return false; + + const uint8_t ksectionSize[kDaikin312Sections] = {kDaikin312Section1Length, + kDaikin312Section2Length}; + // Header/Leader Section + uint64_t leaderdata = 0; + uint16_t used = matchGeneric(results->rawbuf + offset, &leaderdata, + results->rawlen - offset, kDaikinHeaderLength, + 0, 0, // No Header Mark or Space for the "header" + kDaikin312BitMark, kDaikin312OneSpace, + kDaikin312BitMark, kDaikin312ZeroSpace, + kDaikin312BitMark, kDaikin312HdrGap, + false, kDaikinTolerance, 0, false); + if (!used) return false; // Failed to match. + if (leaderdata) return false; // The header bits should all be zero. + + offset += used; + + // Data Sections + uint16_t pos = 0; + for (uint8_t section = 0; section < kDaikin312Sections; section++) { + // Section Header + Section Data + Section Footer + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, ksectionSize[section] * 8, + kDaikin312HdrMark, kDaikin312HdrSpace, + kDaikin312BitMark, kDaikin312OneSpace, + kDaikin312BitMark, kDaikin312ZeroSpace, + kDaikin312BitMark, kDaikin312SectionGap, + section >= kDaikin312Sections - 1, + kDaikinTolerance, 0, false); + if (used == 0) return false; + offset += used; + pos += ksectionSize[section]; + } + // Compliance + if (strict) { + if (pos * 8 != kDaikin312Bits) return false; + } + + // Success + results->decode_type = decode_type_t::DAIKIN312; + 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_DAIKIN312 diff --git a/lib/IRremoteESP8266/src/ir_Daikin.h b/lib/IRremoteESP8266/src/ir_Daikin.h index 0a12c5b69..3a98f07e7 100644 --- a/lib/IRremoteESP8266/src/ir_Daikin.h +++ b/lib/IRremoteESP8266/src/ir_Daikin.h @@ -1,6 +1,6 @@ // Copyright 2016 sillyfrog // Copyright 2017 sillyfrog, crankyoldgit -// Copyright 2018-2021 crankyoldgit +// Copyright 2018-2022 crankyoldgit // Copyright 2019 pasna (IRDaikin160 class / Daikin176 class) /// @file @@ -21,6 +21,8 @@ /// @see Daikin216 https://github.com/crankyoldgit/IRremoteESP8266/issues/689 /// @see Daikin216 https://github.com/danny-source/Arduino_DY_IRDaikin /// @see Daikin64 https://github.com/crankyoldgit/IRremoteESP8266/issues/1064 +/// @see Daikin200 https://github.com/crankyoldgit/IRremoteESP8266/issues/1802 +/// @see Daikin312 https://github.com/crankyoldgit/IRremoteESP8266/issues/1829 // Supports: // Brand: Daikin, Model: ARC433** remote (DAIKIN) @@ -34,19 +36,23 @@ // Brand: Daikin, Model: BRC4C153 remote (DAIKIN176) // Brand: Daikin, Model: FFQ35B8V1B A/C (DAIKIN176) // Brand: Daikin, Model: BRC4C151 remote (DAIKIN176) -// 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: 17 Series FTXB09AXVJU A/C (DAIKIN128) +// Brand: Daikin, Model: 17 Series FTXB12AXVJU A/C (DAIKIN128) +// Brand: Daikin, Model: 17 Series FTXB24AXVJU A/C (DAIKIN128) // Brand: Daikin, Model: BRC52B63 remote (DAIKIN128) // Brand: Daikin, Model: ARC480A5 remote (DAIKIN152) // Brand: Daikin, Model: FFN-C/FCN-F Series A/C (DAIKIN64) // Brand: Daikin, Model: DGS01 remote (DAIKIN64) // Brand: Daikin, Model: M Series A/C (DAIKIN) // Brand: Daikin, Model: FTXM-M A/C (DAIKIN) +// Brand: Daikin, Model: ARC466A12 remote (DAIKIN) // Brand: Daikin, Model: ARC466A33 remote (DAIKIN) // Brand: Daikin, Model: FTWX35AXV1 A/C (DAIKIN64) // Brand: Daikin, Model: ARC484A4 remote (DAIKIN216) // Brand: Daikin, Model: FTQ60TV16U2 A/C (DAIKIN216) +// Brand: Daikin, Model: BRC4M150W16 remote (DAIKIN200) +// Brand: Daikin, Model: FTXM20R5V1B A/C (DAIKIN312) +// Brand: Daikin, Model: ARC466A67 remote (DAIKIN312) #ifndef IR_DAIKIN_H_ #define IR_DAIKIN_H_ @@ -676,6 +682,30 @@ const uint8_t kDaikin64MaxTemp = 30; // Celsius const uint8_t kDaikin64ChecksumOffset = 60; const uint8_t kDaikin64ChecksumSize = 4; // Mask 0b1111 << 59 +const uint16_t kDaikin200Freq = 38000; // Modulation Frequency in Hz. +const uint16_t kDaikin200HdrMark = 4920; +const uint16_t kDaikin200HdrSpace = 2230; +const uint16_t kDaikin200BitMark = 290; +const uint16_t kDaikin200OneSpace = 1850; +const uint16_t kDaikin200ZeroSpace = 780; +const uint16_t kDaikin200Gap = 29400; +const uint16_t kDaikin200Sections = 2; +const uint16_t kDaikin200Section1Length = 7; +const uint16_t kDaikin200Section2Length = kDaikin200StateLength - + kDaikin200Section1Length; + +const uint16_t kDaikin312HdrMark = 3518; +const uint16_t kDaikin312HdrSpace = 1688; +const uint16_t kDaikin312BitMark = 453; +const uint16_t kDaikin312ZeroSpace = 414; +const uint16_t kDaikin312OneSpace = 1275; +const uint16_t kDaikin312HdrGap = 25100; +const uint16_t kDaikin312SectionGap = 35512; +const uint16_t kDaikin312Sections = 2; +const uint16_t kDaikin312Section1Length = 20; +const uint16_t kDaikin312Section2Length = kDaikin312StateLength - + kDaikin312Section1Length; + // Legacy defines. #define DAIKIN_COOL kDaikinCool #define DAIKIN_HEAT kDaikinHeat diff --git a/lib/IRremoteESP8266/src/ir_Delonghi.cpp b/lib/IRremoteESP8266/src/ir_Delonghi.cpp index 21a787799..19dec3741 100644 --- a/lib/IRremoteESP8266/src/ir_Delonghi.cpp +++ b/lib/IRremoteESP8266/src/ir_Delonghi.cpp @@ -421,7 +421,7 @@ uint16_t IRDelonghiAc::getOffTimer(void) const { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRDelonghiAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::DELONGHI_AC; result.power = _.Power; // result.mode = toCommonMode(getMode()); diff --git a/lib/IRremoteESP8266/src/ir_Ecoclim.cpp b/lib/IRremoteESP8266/src/ir_Ecoclim.cpp index b16f8beb2..983913d38 100644 --- a/lib/IRremoteESP8266/src/ir_Ecoclim.cpp +++ b/lib/IRremoteESP8266/src/ir_Ecoclim.cpp @@ -359,7 +359,7 @@ void IREcoclimAc::disableOffTimer(void) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IREcoclimAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::ECOCLIM; result.power = _.Power; result.mode = toCommonMode(getMode()); diff --git a/lib/IRremoteESP8266/src/ir_Electra.cpp b/lib/IRremoteESP8266/src/ir_Electra.cpp index b4d670b88..f0882bc6d 100644 --- a/lib/IRremoteESP8266/src/ir_Electra.cpp +++ b/lib/IRremoteESP8266/src/ir_Electra.cpp @@ -359,7 +359,7 @@ uint8_t IRElectraAc::getSensorTemp(void) const { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRElectraAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::ELECTRA_AC; result.power = _.Power; result.mode = toCommonMode(_.Mode); diff --git a/lib/IRremoteESP8266/src/ir_Electra.h b/lib/IRremoteESP8266/src/ir_Electra.h index 8fd4ee182..142b52562 100644 --- a/lib/IRremoteESP8266/src/ir_Electra.h +++ b/lib/IRremoteESP8266/src/ir_Electra.h @@ -13,6 +13,7 @@ // Brand: Subtropic, Model: YKR-H/102E remote // Brand: Centek, Model: SCT-65Q09 A/C // Brand: Centek, Model: YKR-P/002E remote +// Brand: AEG, Model: Chillflex Pro AXP26U338CW A/C #ifndef IR_ELECTRA_H_ #define IR_ELECTRA_H_ diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp index da4b41dcf..0c2a84331 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.cpp +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.cpp @@ -1,5 +1,5 @@ // Copyright 2017 Jonny Graham -// Copyright 2017-2021 David Conran +// Copyright 2017-2022 David Conran // Copyright 2021 siriuslzx /// @file @@ -116,6 +116,7 @@ void IRFujitsuAC::stateReset(void) { _.longcode[1] = 0x63; _.longcode[3] = 0x10; _.longcode[4] = 0x10; + _rawstatemodified = true; } /// Set up hardware to be able to send a message. @@ -142,6 +143,7 @@ bool IRFujitsuAC::updateUseLongOrShort(void) { case kFujitsuAcCmdStepHoriz: // 0x79 case kFujitsuAcCmdToggleSwingHoriz: // 0x7A _.Cmd = _cmd; + _rawstatemodified = true; break; default: switch (_model) { @@ -150,10 +152,12 @@ bool IRFujitsuAC::updateUseLongOrShort(void) { case fujitsu_ac_remote_model_t::ARREB1E: case fujitsu_ac_remote_model_t::ARREW4E: _.Cmd = 0xFE; + _rawstatemodified = true; break; case fujitsu_ac_remote_model_t::ARDB1: case fujitsu_ac_remote_model_t::ARJW2: _.Cmd = 0xFC; + _rawstatemodified = true; break; } fullCmd = true; @@ -164,7 +168,8 @@ bool IRFujitsuAC::updateUseLongOrShort(void) { /// Calculate and set the checksum values for the internal state. void IRFujitsuAC::checkSum(void) { - if (updateUseLongOrShort()) { // Is it a long code? + _rawstatemodified = true; + if (updateUseLongOrShort()) { // Is it going to be a long code? // Nr. of bytes in the message after this byte. _.RestLength = _state_length - 7; _.Protocol = (_model == fujitsu_ac_remote_model_t::ARREW4E) ? 0x31 : 0x30; @@ -179,7 +184,13 @@ void IRFujitsuAC::checkSum(void) { } } if (_model != fujitsu_ac_remote_model_t::ARRY4) { - if (_model != fujitsu_ac_remote_model_t::ARREW4E) _.Clean = false; + switch (_model) { + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREW4E: + break; + default: + _.Clean = false; + } _.Filter = false; } // Set the On/Off/Sleep timer Nr of mins. @@ -235,13 +246,17 @@ uint8_t IRFujitsuAC::getStateLength(void) { return updateUseLongOrShort() ? _state_length : _state_length_short; } +/// Is the current binary state representation a long or a short code? +/// @return true, if long; false, if short. +bool IRFujitsuAC::isLongCode(void) const { + return _.Cmd == 0xFE || _.Cmd == 0xFC; +} + /// Get a PTR to the internal state/code for this protocol. /// @return PTR to a code for this protocol based on the current internal state. uint8_t* IRFujitsuAC::getRaw(void) { checkSum(); - if (_.Cmd == 0xFE || _.Cmd == 0xFC) - return _.longcode; - return _.shortcode; + return isLongCode() ? _.longcode : _.shortcode; } /// Build the internal state/config from the current (raw) A/C message. @@ -281,7 +296,8 @@ void IRFujitsuAC::buildFromState(const uint16_t length) { setCmd(kFujitsuAcCmdStayOn); // Currently the only way we know how to tell ARRAH2E & ARRY4 apart is if // either the raw Filter or Clean setting is on. - if (_model == fujitsu_ac_remote_model_t::ARRAH2E && (_.Filter || _.Clean)) + if (_model == fujitsu_ac_remote_model_t::ARRAH2E && (_.Filter || _.Clean) && + !get10CHeat()) setModel(fujitsu_ac_remote_model_t::ARRY4); if (_state_length == kFujitsuAcStateLength && _.OutsideQuiet) setModel(fujitsu_ac_remote_model_t::ARREB1E); @@ -312,6 +328,7 @@ bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { _.longcode[i] = 0; } buildFromState(length); + _rawstatemodified = false; return true; } @@ -405,6 +422,7 @@ bool IRFujitsuAC::getPower(void) const { return _cmd != kFujitsuAcCmdTurnOff; } /// @param[in] on true, the setting is on. false, the setting is off. void IRFujitsuAC::setOutsideQuiet(const bool on) { _.OutsideQuiet = on; + _rawstatemodified = true; setCmd(kFujitsuAcCmdStayOn); // No special command involved. } @@ -461,6 +479,7 @@ void IRFujitsuAC::setTemp(const float temp, const bool useCelsius) { } else { _.Temp = _temp - offset; } + _rawstatemodified = true; setCmd(kFujitsuAcCmdStayOn); // No special command involved. } @@ -484,6 +503,7 @@ void IRFujitsuAC::setFanSpeed(const uint8_t fanSpeed) { _.Fan = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. else _.Fan = fanSpeed; + _rawstatemodified = true; setCmd(kFujitsuAcCmdStayOn); // No special command involved. } @@ -498,6 +518,7 @@ void IRFujitsuAC::setMode(const uint8_t mode) { _.Mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. else _.Mode = mode; + _rawstatemodified = true; setCmd(kFujitsuAcCmdStayOn); // No special command involved. } @@ -511,6 +532,7 @@ uint8_t IRFujitsuAC::getMode(void) const { return _.Mode; } /// @note Not all models support all possible swing modes. void IRFujitsuAC::setSwing(const uint8_t swingMode) { _.Swing = swingMode; + _rawstatemodified = true; switch (_model) { // No Horizontal support. case fujitsu_ac_remote_model_t::ARDB1: @@ -531,14 +553,13 @@ void IRFujitsuAC::setSwing(const uint8_t swingMode) { /// Get the requested swing operation mode of the A/C unit. /// @return The contents of the swing state/mode. -uint8_t IRFujitsuAC::getSwing(void) const { - return _.Swing; -} +uint8_t IRFujitsuAC::getSwing(void) const { return _.Swing; } /// Set the Clean mode of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. void IRFujitsuAC::setClean(const bool on) { _.Clean = on; + _rawstatemodified = true; setCmd(kFujitsuAcCmdStayOn); // No special command involved. } @@ -555,6 +576,7 @@ bool IRFujitsuAC::getClean(void) const { /// @param[in] on true, the setting is on. false, the setting is off. void IRFujitsuAC::setFilter(const bool on) { _.Filter = on; + _rawstatemodified = true; setCmd(kFujitsuAcCmdStayOn); // No special command involved. } @@ -563,7 +585,7 @@ void IRFujitsuAC::setFilter(const bool on) { bool IRFujitsuAC::getFilter(void) const { switch (_model) { case fujitsu_ac_remote_model_t::ARRY4: return _.Filter; - default: return false; + default: return false; } } @@ -572,6 +594,7 @@ bool IRFujitsuAC::getFilter(void) const { void IRFujitsuAC::set10CHeat(const bool on) { switch (_model) { // Only selected models support this. + case fujitsu_ac_remote_model_t::ARRAH2E: case fujitsu_ac_remote_model_t::ARREW4E: setClean(on); // 10C Heat uses the same bit as Clean if (on) { @@ -579,6 +602,7 @@ void IRFujitsuAC::set10CHeat(const bool on) { _.Power = true; _.Fan = kFujitsuAcFanAuto; _.Swing = kFujitsuAcSwingOff; + _rawstatemodified = true; } default: break; @@ -589,6 +613,7 @@ void IRFujitsuAC::set10CHeat(const bool on) { /// @return true, the setting is on. false, the setting is off. bool IRFujitsuAC::get10CHeat(void) const { switch (_model) { + case fujitsu_ac_remote_model_t::ARRAH2E: case fujitsu_ac_remote_model_t::ARREW4E: return (_.Clean && _.Power && _.Mode == kFujitsuAcModeFan && _.Fan == kFujitsuAcFanAuto && _.Swing == kFujitsuAcSwingOff); @@ -602,9 +627,8 @@ uint8_t IRFujitsuAC::getTimerType(void) const { switch (_model) { // These models seem to have timer support. case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - return _.TimerType; - default: return kFujitsuAcStopTimers; + case fujitsu_ac_remote_model_t::ARREB1E: return _.TimerType; + default: return kFujitsuAcStopTimers; } } @@ -620,6 +644,7 @@ void IRFujitsuAC::setTimerType(const uint8_t timertype) { break; default: _.TimerType = kFujitsuAcStopTimers; } + _rawstatemodified = true; } /// Get the On Timer setting of the A/C. @@ -634,6 +659,7 @@ uint16_t IRFujitsuAC::getOnTimer(void) const { /// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. void IRFujitsuAC::setOnTimer(const uint16_t nr_mins) { _.OnTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. + _rawstatemodified = true; if (_.OnTimer) { _.TimerType = kFujitsuAcOnTimer; } else if (getTimerType() == kFujitsuAcOnTimer) { @@ -646,10 +672,8 @@ void IRFujitsuAC::setOnTimer(const uint16_t nr_mins) { uint16_t IRFujitsuAC::getOffSleepTimer(void) const { switch (getTimerType()) { case kFujitsuAcOffTimer: - case kFujitsuAcSleepTimer: - return _.OffTimer; - default: - return 0; + case kFujitsuAcSleepTimer: return _.OffTimer; + default: return 0; } } @@ -657,12 +681,13 @@ uint16_t IRFujitsuAC::getOffSleepTimer(void) const { /// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. inline void IRFujitsuAC::setOffSleepTimer(const uint16_t nr_mins) { _.OffTimer = std::min(kFujitsuAcTimerMax, nr_mins); // Bounds check. + _rawstatemodified = true; } /// Set the Off Timer time for the A/C. /// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. void IRFujitsuAC::setOffTimer(const uint16_t nr_mins) { - setOffSleepTimer(nr_mins); + setOffSleepTimer(nr_mins); // This will also set _rawstatemodified to true. if (nr_mins) _.TimerType = kFujitsuAcOffTimer; else if (getTimerType() != kFujitsuAcOnTimer) @@ -672,7 +697,7 @@ void IRFujitsuAC::setOffTimer(const uint16_t nr_mins) { /// Set the Sleep Timer time for the A/C. /// @param[in] nr_mins Nr. of minutes to set the timer to. 0 means disabled. void IRFujitsuAC::setSleepTimer(const uint16_t nr_mins) { - setOffSleepTimer(nr_mins); + setOffSleepTimer(nr_mins); // This will also set _rawstatemodified to true. if (nr_mins) _.TimerType = kFujitsuAcSleepTimer; else if (getTimerType() != kFujitsuAcOnTimer) @@ -706,7 +731,10 @@ bool IRFujitsuAC::validChecksum(uint8_t state[], const uint16_t length) { /// Set the device's remote ID number. /// @param[in] num The ID for the remote. Valid number range is 0 to 3. -void IRFujitsuAC::setId(const uint8_t num) { _.Id = num; } +void IRFujitsuAC::setId(const uint8_t num) { + _.Id = num; + _rawstatemodified = true; +} /// Get the current device's remote ID number. /// @return The current device's remote ID number. @@ -714,7 +742,10 @@ uint8_t IRFujitsuAC::getId(void) const { return _.Id; } /// Set the Temperature units for the A/C. /// @param[in] on true, use Celsius. false, use Fahrenheit. -void IRFujitsuAC::setCelsius(const bool on) { _.Fahrenheit = !on; } +void IRFujitsuAC::setCelsius(const bool on) { + _.Fahrenheit = !on; + _rawstatemodified = true; +} /// Get the Clean mode status of the A/C. /// @return true, the setting is on. false, the setting is off. @@ -774,35 +805,45 @@ stdAc::fanspeed_t IRFujitsuAC::toCommonFanSpeed(const uint8_t speed) { } /// Convert the current internal state into its stdAc::state_t equivalent. +/// @param[in] prev Ptr to a previous state. /// @return The stdAc equivalent of the native settings. -stdAc::state_t IRFujitsuAC::toCommon(void) const { - stdAc::state_t result; +stdAc::state_t IRFujitsuAC::toCommon(const stdAc::state_t *prev) { + stdAc::state_t result{}; + if (prev != NULL) result = *prev; result.protocol = decode_type_t::FUJITSU_AC; + checkSum(); result.model = _model; result.power = getPower(); - result.mode = toCommonMode(_.Mode); - result.celsius = getCelsius(); - result.degrees = getTemp(); - result.fanspeed = toCommonFanSpeed(_.Fan); - uint8_t swing = _.Swing; - switch (result.model) { - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARRY4: - result.clean = _.Clean; - result.filter = _.Filter; - 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; + // Only update these settings if it is a long message, or we have no previous + // state info for those settings. + if (isLongCode() || prev == NULL) { + result.mode = toCommonMode(_.Mode); + result.celsius = getCelsius(); + { + const float minHeat = result.celsius ? kFujitsuAcMinHeat + : kFujitsuAcMinHeatF; + result.degrees = get10CHeat() ? minHeat : getTemp(); + } + result.fanspeed = toCommonFanSpeed(_.Fan); + uint8_t swing = _.Swing; + switch (result.model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARRY4: + result.clean = _.Clean; + result.filter = _.Filter; + 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 = _.Fan == kFujitsuAcFanQuiet; result.turbo = _cmd == kFujitsuAcCmdPowerful; result.econo = _cmd == kFujitsuAcCmdEcono; @@ -820,54 +861,67 @@ stdAc::state_t IRFujitsuAC::toCommon(void) const { /// @return A human readable string. String IRFujitsuAC::toString(void) const { String result = ""; - result.reserve(100); // Reserve some heap for the string to reduce fragging. + result.reserve(180); // Reserve some heap for the string to reduce fragging. fujitsu_ac_remote_model_t model = _model; result += addModelToString(decode_type_t::FUJITSU_AC, model, false); result += addIntToString(_.Id, kIdStr); result += addBoolToString(getPower(), kPowerStr); - result += addModeToString(_.Mode, kFujitsuAcModeAuto, kFujitsuAcModeCool, - kFujitsuAcModeHeat, kFujitsuAcModeDry, - kFujitsuAcModeFan); - result += addTempFloatToString(getTemp(), getCelsius()); - result += addFanToString(_.Fan, kFujitsuAcFanHigh, kFujitsuAcFanLow, - kFujitsuAcFanAuto, kFujitsuAcFanQuiet, - kFujitsuAcFanMed); - switch (model) { - // These models have no internal swing, clean. or filter state. - case fujitsu_ac_remote_model_t::ARDB1: - case fujitsu_ac_remote_model_t::ARJW2: - break; - // These models have Clean & Filter, plus Swing (via fall thru) - case fujitsu_ac_remote_model_t::ARRAH2E: - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARRY4: - result += addBoolToString(getClean(), kCleanStr); - result += addBoolToString(getFilter(), kFilterStr); - // FALL THRU - default: // e.g. ARREW4E - if (model == fujitsu_ac_remote_model_t::ARREW4E) - result += addBoolToString(get10CHeat(), k10CHeatStr); - result += addIntToString(_.Swing, kSwingStr); - result += kSpaceLBraceStr; - switch (_.Swing) { - case kFujitsuAcSwingOff: - result += kOffStr; - break; - case kFujitsuAcSwingVert: - result += kSwingVStr; - break; - case kFujitsuAcSwingHoriz: - result += kSwingHStr; - break; - case kFujitsuAcSwingBoth: - result += kSwingVStr; - result += '+'; - result += kSwingHStr; - break; - default: - result += kUnknownStr; - } - result += ')'; + if (_rawstatemodified || isLongCode()) { + result += addModeToString(_.Mode, kFujitsuAcModeAuto, kFujitsuAcModeCool, + kFujitsuAcModeHeat, kFujitsuAcModeDry, + kFujitsuAcModeFan); + { + const bool isCelsius = getCelsius(); + const float minHeat = isCelsius ? kFujitsuAcMinHeat : kFujitsuAcMinHeatF; + result += addTempFloatToString(get10CHeat() ? minHeat : getTemp(), + isCelsius); + } + result += addFanToString(_.Fan, kFujitsuAcFanHigh, kFujitsuAcFanLow, + kFujitsuAcFanAuto, kFujitsuAcFanQuiet, + kFujitsuAcFanMed); + switch (model) { + // These models have no internal swing, clean. or filter state. + case fujitsu_ac_remote_model_t::ARDB1: + case fujitsu_ac_remote_model_t::ARJW2: + break; + // These models have Clean & Filter, plus Swing (via fall thru) + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARRY4: + result += addBoolToString(getClean(), kCleanStr); + result += addBoolToString(getFilter(), kFilterStr); + // FALL THRU + default: // e.g. ARREW4E + switch (model) { + case fujitsu_ac_remote_model_t::ARRAH2E: + case fujitsu_ac_remote_model_t::ARREW4E: + result += addBoolToString(get10CHeat(), k10CHeatStr); + break; + default: + break; + } + result += addIntToString(_.Swing, kSwingStr); + result += kSpaceLBraceStr; + switch (_.Swing) { + case kFujitsuAcSwingOff: + result += kOffStr; + break; + case kFujitsuAcSwingVert: + result += kSwingVStr; + break; + case kFujitsuAcSwingHoriz: + result += kSwingHStr; + break; + case kFujitsuAcSwingBoth: + result += kSwingVStr; + result += '+'; + result += kSwingHStr; + break; + default: + result += kUnknownStr; + } + result += ')'; + } } result += kCommaSpaceStr; result += kCommandStr; @@ -902,33 +956,36 @@ String IRFujitsuAC::toString(void) const { default: result += kNAStr; } - uint16_t mins = 0; - String type_str = kTimerStr; - switch (model) { - case fujitsu_ac_remote_model_t::ARREB1E: - case fujitsu_ac_remote_model_t::ARREW4E: - result += addBoolToString(getOutsideQuiet(), kOutsideQuietStr); - // FALL THRU - // These models seem to have timer support. - case fujitsu_ac_remote_model_t::ARRAH2E: - switch (getTimerType()) { - case kFujitsuAcOnTimer: - type_str = kOnTimerStr; - mins = getOnTimer(); - break; - case kFujitsuAcOffTimer: - type_str = kOffTimerStr; - mins = getOffSleepTimer(); - break; - case kFujitsuAcSleepTimer: - type_str = kSleepTimerStr; - mins = getOffSleepTimer(); - break; - } - result += addLabeledString(mins ? minsToString(mins) : kOffStr, type_str); - break; - default: - break; + if (_rawstatemodified || isLongCode()) { + uint16_t mins = 0; + String type_str = kTimerStr; + switch (model) { + case fujitsu_ac_remote_model_t::ARREB1E: + case fujitsu_ac_remote_model_t::ARREW4E: + result += addBoolToString(getOutsideQuiet(), kOutsideQuietStr); + // FALL THRU + // These models seem to have timer support. + case fujitsu_ac_remote_model_t::ARRAH2E: + switch (getTimerType()) { + case kFujitsuAcOnTimer: + type_str = kOnTimerStr; + mins = getOnTimer(); + break; + case kFujitsuAcOffTimer: + type_str = kOffTimerStr; + mins = getOffSleepTimer(); + break; + case kFujitsuAcSleepTimer: + type_str = kSleepTimerStr; + mins = getOffSleepTimer(); + break; + } + result += addLabeledString(mins ? minsToString(mins) : kOffStr, + type_str); + break; + default: + break; + } } return result; } diff --git a/lib/IRremoteESP8266/src/ir_Fujitsu.h b/lib/IRremoteESP8266/src/ir_Fujitsu.h index 70c0a4cf0..6e2583a0c 100644 --- a/lib/IRremoteESP8266/src/ir_Fujitsu.h +++ b/lib/IRremoteESP8266/src/ir_Fujitsu.h @@ -1,5 +1,5 @@ // Copyright 2017 Jonny Graham -// Copyright 2018-2021 David Conran +// Copyright 2018-2022 David Conran // Copyright 2021 siriuslzx /// @file @@ -30,6 +30,7 @@ // Brand: Fujitsu, Model: AR-DL10 remote (ARDB1) // Brand: Fujitsu, Model: ASU30C1 A/C (ARDB1) // Brand: Fujitsu, Model: AR-RAH1U remote (ARREB1E) +// Brand: Fujitsu, Model: AR-RAH2U remote (ARRAH2E) // Brand: Fujitsu, Model: ASU12RLF A/C (ARREB1E) // Brand: Fujitsu, Model: AR-REW4E remote (ARREW4E) // Brand: Fujitsu, Model: ASYG09KETA-B A/C (ARREW4E) @@ -37,6 +38,9 @@ // Brand: Fujitsu, Model: ASTG09K A/C (ARREW4E) // Brand: Fujitsu, Model: ASTG18K A/C (ARREW4E) // Brand: Fujitsu, Model: AR-REW1E remote (ARREW4E) +// Brand: Fujitsu, Model: AR-REG1U remote (ARRAH2E) +// Brand: OGeneral, Model: AR-RCL1E remote (ARRAH2E) +// Brand: Fujitsu General, Model: AR-JW17 remote (ARDB1) #ifndef IR_FUJITSU_H_ #define IR_FUJITSU_H_ @@ -128,9 +132,11 @@ const uint8_t kFujitsuAcFanMed = 0x02; const uint8_t kFujitsuAcFanLow = 0x03; const uint8_t kFujitsuAcFanQuiet = 0x04; +const float kFujitsuAcMinHeat = 10; // 10C const float kFujitsuAcMinTemp = 16; // 16C const float kFujitsuAcMaxTemp = 30; // 30C const uint8_t kFujitsuAcTempOffsetC = kFujitsuAcMinTemp; +const float kFujitsuAcMinHeatF = 50; // 50F const float kFujitsuAcMinTempF = 60; // 60F const float kFujitsuAcMaxTempF = 88; // 88F const uint8_t kFujitsuAcTempOffsetF = 44; @@ -206,6 +212,7 @@ class IRFujitsuAC { bool setRaw(const uint8_t newState[], const uint16_t length); uint8_t getStateLength(void); static bool validChecksum(uint8_t* state, const uint16_t length); + bool isLongCode(void) const; void setPower(const bool on); void off(void); void on(void); @@ -233,7 +240,7 @@ class IRFujitsuAC { static uint8_t convertFan(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) const; + stdAc::state_t toCommon(const stdAc::state_t *prev = NULL); String toString(void) const; #ifndef UNIT_TEST @@ -249,6 +256,7 @@ class IRFujitsuAC { fujitsu_ac_remote_model_t _model; uint8_t _state_length; uint8_t _state_length_short; + bool _rawstatemodified; void checkSum(void); bool updateUseLongOrShort(void); void buildFromState(const uint16_t length); diff --git a/lib/IRremoteESP8266/src/ir_Goodweather.cpp b/lib/IRremoteESP8266/src/ir_Goodweather.cpp index 1d6918e7f..c3d9d62c8 100644 --- a/lib/IRremoteESP8266/src/ir_Goodweather.cpp +++ b/lib/IRremoteESP8266/src/ir_Goodweather.cpp @@ -311,7 +311,7 @@ stdAc::fanspeed_t IRGoodweatherAc::toCommonFanSpeed(const uint8_t speed) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRGoodweatherAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::GOODWEATHER; result.power = _.Power; result.mode = toCommonMode(_.Mode); diff --git a/lib/IRremoteESP8266/src/ir_Gree.cpp b/lib/IRremoteESP8266/src/ir_Gree.cpp index 3c3a8583e..ead7178e3 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.cpp +++ b/lib/IRremoteESP8266/src/ir_Gree.cpp @@ -162,6 +162,7 @@ void IRGreeAC::setRaw(const uint8_t new_code[]) { else _model = gree_ac_remote_model_t::YBOFB; } + if (_.Mode == kGreeEcono) _model = gree_ac_remote_model_t::YX1FSF; } /// Calculate and set the checksum values for the internal state. @@ -186,7 +187,8 @@ bool IRGreeAC::validChecksum(const uint8_t state[], const uint16_t length) { 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; + case gree_ac_remote_model_t::YBOFB: + case gree_ac_remote_model_t::YX1FSF: _model = model; break; default: _model = gree_ac_remote_model_t::YAW1F; } } @@ -291,6 +293,7 @@ void IRGreeAC::setMode(const uint8_t new_mode) { case kGreeDry: setFan(1); break; case kGreeCool: case kGreeFan: + case kGreeEcono: case kGreeHeat: break; // If we get an unexpected mode, default to AUTO. default: mode = kGreeAuto; @@ -352,11 +355,17 @@ bool IRGreeAC::getTurbo(void) const { return _.Turbo; } /// Set the Econo setting of the A/C. /// @param[in] on true, the setting is on. false, the setting is off. -void IRGreeAC::setEcono(const bool on) { _.Econo = on; } +void IRGreeAC::setEcono(const bool on) { + _.Econo = on; + if (on && getModel() == gree_ac_remote_model_t::YX1FSF) + setMode(kGreeEcono); +} /// Get the Econo setting of the A/C. /// @return true, the setting is on. false, the setting is off. -bool IRGreeAC::getEcono(void) const { return _.Econo; } +bool IRGreeAC::getEcono(void) const { + return _.Econo || getMode() == kGreeEcono; +} /// Set the Vertical Swing mode of the A/C. /// @param[in] automatic Do we use the automatic setting? @@ -575,7 +584,7 @@ stdAc::swingh_t IRGreeAC::toCommonSwingH(const uint8_t pos) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRGreeAC::toCommon(void) { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::GREE; result.model = _model; result.power = _.Power; @@ -589,7 +598,7 @@ stdAc::state_t IRGreeAC::toCommon(void) { result.swingv = toCommonSwingV(_.SwingV); result.swingh = toCommonSwingH(_.SwingH); result.turbo = _.Turbo; - result.econo = _.Econo; + result.econo = getEcono(); result.light = _.Light; result.clean = _.Xfan; result.sleep = _.Sleep ? 0 : -1; @@ -608,8 +617,15 @@ String IRGreeAC::toString(void) { result.reserve(220); // Reserve some heap for the string to reduce fragging. result += addModelToString(decode_type_t::GREE, _model, false); result += addBoolToString(_.Power, kPowerStr); - result += addModeToString(_.Mode, kGreeAuto, kGreeCool, kGreeHeat, - kGreeDry, kGreeFan); + if (_model == gree_ac_remote_model_t::YX1FSF && _.Mode == kGreeEcono) { + result += addIntToString(_.Mode, kModeStr); + result += kSpaceLBraceStr; + result += kEconoStr; + result += ')'; + } else { + result += addModeToString(_.Mode, kGreeAuto, kGreeCool, kGreeHeat, + kGreeDry, kGreeFan); + } result += addTempToString(getTemp(), !_.UseFahrenheit); result += addFanToString(_.Fan, kGreeFanMax, kGreeFanMin, kGreeFanAuto, kGreeFanAuto, kGreeFanMed); diff --git a/lib/IRremoteESP8266/src/ir_Gree.h b/lib/IRremoteESP8266/src/ir_Gree.h index be5ac31ce..eb9d4a00c 100644 --- a/lib/IRremoteESP8266/src/ir_Gree.h +++ b/lib/IRremoteESP8266/src/ir_Gree.h @@ -1,9 +1,10 @@ -// Copyright 2016 David Conran +// Copyright 2016-2022 David Conran /// @file /// @brief Support for Gree A/C protocols. /// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1508 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1821 // Supports: // Brand: Ultimate, Model: Heat Pump @@ -15,6 +16,7 @@ // Brand: Gree, Model: YAA1FBF remote // Brand: Gree, Model: YB1F2F remote // Brand: Gree, Model: YAN1F1 remote +// Brand: Gree, Model: YX1F2F remote (YX1FSF) // Brand: Gree, Model: VIR09HP115V1AH A/C // Brand: Gree, Model: VIR12HP230V1AH A/C // Brand: Amana, Model: PBC093G00CC A/C @@ -23,6 +25,7 @@ // Brand: Cooper & Hunter, Model: CH-S09FTXG A/C // Brand: Vailland, Model: YACIFB remote // Brand: Vailland, Model: VAI5-035WNI A/C +// Brand: Soleus Air, Model: window A/C (YX1FSF) #ifndef IR_GREE_H_ #define IR_GREE_H_ @@ -86,11 +89,12 @@ union GreeProtocol{ // 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; +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; +const uint8_t kGreeEcono = 5; const uint8_t kGreeFanAuto = 0; const uint8_t kGreeFanMin = 1; diff --git a/lib/IRremoteESP8266/src/ir_Haier.cpp b/lib/IRremoteESP8266/src/ir_Haier.cpp index 5c9f2a36d..93336212e 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.cpp +++ b/lib/IRremoteESP8266/src/ir_Haier.cpp @@ -46,7 +46,8 @@ using irutils::minsToString; _.x##Mins = mins % 60;\ } while (0) -#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176) +#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC160 || \ + SEND_HAIER_AC176) /// Send a Haier A/C formatted message. (HSU07-HEA03 remote) /// Status: STABLE / Known to be working. /// @param[in] data The message to be sent. @@ -93,6 +94,18 @@ void IRsend::sendHaierAC176(const unsigned char data[], const uint16_t nbytes, } #endif // SEND_HAIER_AC176 +#if SEND_HAIER_AC160 +/// Send a Haier 160 bit remote A/C formatted message. +/// Status: STABLE / Known to be working. +/// @param[in] data The message to be sent. +/// @param[in] nbytes The number of bytes of message to be sent. +/// @param[in] repeat The number of times the command is to be repeated. +void IRsend::sendHaierAC160(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes >= kHaierAC160StateLength) sendHaierAC(data, nbytes, repeat); +} +#endif // SEND_HAIER_AC160 + /// Class constructor /// @param[in] pin GPIO to be used when sending. /// @param[in] inverted Is the output signal to be inverted? @@ -426,7 +439,7 @@ stdAc::swingv_t IRHaierAC::toCommonSwingV(const uint8_t pos) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRHaierAC::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::HAIER_AC; result.model = -1; // No models used. result.power = true; @@ -568,7 +581,7 @@ void IRHaierAC176::checksum(void) { /// @return true, if the state has a valid checksum. Otherwise, false. bool IRHaierAC176::validChecksum(const uint8_t state[], const uint16_t length) { if (length < 2) return false; // 1 byte of data can't have a checksum. - if (length < kHaierAC176StateLength) { // Is it too short? + if (length < kHaierAC160StateLength) { // Is it too short? // Then it is just a checksum of the whole thing. return (state[length - 1] == sumBytes(state, length - 1)); } else { // It is long enough for two checksums. @@ -1127,7 +1140,7 @@ stdAc::swingh_t IRHaierAC176::toCommonSwingH(const uint8_t pos) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRHaierAC176::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::HAIER_AC_YRW02; result.model = getModel(); result.power = _.Power; @@ -1323,7 +1336,8 @@ bool IRHaierACYRW02::validChecksum(const uint8_t state[], } // End of IRHaierACYRW02 class. -#if (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02) +#if (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02 || DECODE_HAIER_AC160 || \ + DECODE_HAIER_AC176) /// Decode the supplied Haier HSU07-HEA03 remote message. /// Status: STABLE / Known to be working. /// @param[in,out] results Ptr to the data to decode & where to store the decode @@ -1435,3 +1449,35 @@ bool IRrecv::decodeHaierAC176(decode_results* results, uint16_t offset, return true; } #endif // DECODE_HAIER_AC176 + +#if DECODE_HAIER_AC160 +/// Decode the supplied Haier 160 bit remote A/C message. +/// Status: STABLE / Known to be working. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// result. +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +bool IRrecv::decodeHaierAC160(decode_results* results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict) { + if (nbits != kHaierAC160Bits) + return false; // Not strictly a HAIER_AC160 message. + } + + // The protocol is almost exactly the same as HAIER_AC + if (!decodeHaierAC(results, offset, nbits, false)) return false; + + // Compliance + if (strict) { + if (!IRHaierAC176::validChecksum(results->state, nbits / 8)) return false; + } + + // Success + // It looks correct, but we haven't check the checksum etc. + results->decode_type = HAIER_AC160; + return true; +} +#endif // DECODE_HAIER_AC160 diff --git a/lib/IRremoteESP8266/src/ir_Haier.h b/lib/IRremoteESP8266/src/ir_Haier.h index 4e5c8e64c..9afc862a5 100644 --- a/lib/IRremoteESP8266/src/ir_Haier.h +++ b/lib/IRremoteESP8266/src/ir_Haier.h @@ -8,6 +8,7 @@ /// @see https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0 /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/485 /// @see https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1804 // Supports: // Brand: Haier, Model: HSU07-HEA03 remote (HAIER_AC) @@ -17,6 +18,7 @@ // Brand: Mabe, Model: MMI18HDBWCA6MI8 A/C (HAIER_AC176) // Brand: Mabe, Model: V12843 HJ200223 remote (HAIER_AC176) // Brand: Daichi, Model: D-H A/C (HAIER_AC176) +// Brand: Haier, Model: KFR-26GW/83@UI-Ge A/C (HAIER_AC160) #ifndef IR_HAIER_H_ #define IR_HAIER_H_ diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.cpp b/lib/IRremoteESP8266/src/ir_Hitachi.cpp index 49781997e..e47bac929 100644 --- a/lib/IRremoteESP8266/src/ir_Hitachi.cpp +++ b/lib/IRremoteESP8266/src/ir_Hitachi.cpp @@ -7,6 +7,7 @@ /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1056 /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1060 /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1757 #include "ir_Hitachi.h" #include @@ -56,7 +57,8 @@ using irutils::checkInvertedBytePairs; using irutils::invertBytePairs; using irutils::minsToString; -#if (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344) +#if (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC264 || \ + SEND_HITACHI_AC344) /// Send a Hitachi 28-byte/224-bit A/C formatted message. (HITACHI_AC) /// Status: STABLE / Working. /// @param[in] data The message to be sent. @@ -68,13 +70,21 @@ void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes, if (nbytes < kHitachiAcStateLength) return; // Not enough bytes to send a proper message. - const bool MSBfirst = (nbytes == kHitachiAc344StateLength) ? false : true; + bool MSBfirst = true; + switch (nbytes) { + case kHitachiAc264StateLength: + case kHitachiAc296StateLength: + case kHitachiAc344StateLength: + MSBfirst = false; + } + sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark, kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace, kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, 38, MSBfirst, repeat, 50); } -#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344) +#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC264 || + // SEND_HITACHI_AC344) #if SEND_HITACHI_AC1 /// Send a Hitachi 13 byte/224-bit A/C formatted message. (HITACHI_AC1) @@ -82,7 +92,7 @@ void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes, /// @param[in] data The message to be sent. /// @param[in] nbytes The number of bytes of message to be sent. /// @param[in] repeat The number of times the command is to be repeated. -/// @note Basically the same as sendHitatchiAC() except different size & header. +/// @note Basically the same as sendHitachiAC() except different size & header. /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453 void IRsend::sendHitachiAC1(const unsigned char data[], const uint16_t nbytes, const uint16_t repeat) { @@ -97,7 +107,7 @@ void IRsend::sendHitachiAC1(const unsigned char data[], const uint16_t nbytes, #if SEND_HITACHI_AC2 /// Send a Hitachi 53 byte/424-bit A/C formatted message. (HITACHI_AC2) -/// Basically the same as sendHitatchiAC() except different size. +/// Basically the same as sendHitachiAC() except different size. /// Status: STABLE / Expected to work. /// @param[in] data The message to be sent. /// @param[in] nbytes The number of bytes of message to be sent. @@ -112,7 +122,7 @@ void IRsend::sendHitachiAC2(const unsigned char data[], const uint16_t nbytes, #if SEND_HITACHI_AC344 /// Send a Hitachi A/C 43-byte/344-bit message. (HITACHI_AC344) -/// Basically the same as sendHitatchiAC() except different size. +/// Basically the same as sendHitachiAC() except different size. /// Status: Beta / Probably works. /// @param[in] data An array of bytes containing the IR command. /// @param[in] nbytes Nr. of bytes of data in the array. @@ -373,7 +383,7 @@ stdAc::fanspeed_t IRHitachiAc::toCommonFanSpeed(const uint8_t speed) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRHitachiAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::HITACHI_AC; result.model = -1; // No models used. result.power = _.Power; @@ -773,7 +783,7 @@ stdAc::fanspeed_t IRHitachiAc1::toCommonFanSpeed(const uint8_t speed) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRHitachiAc1::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::HITACHI_AC1; result.model = getModel(); result.power = _.Power; @@ -827,7 +837,7 @@ String IRHitachiAc1::toString(void) const { } #if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || \ - DECODE_HITACHI_AC344) + DECODE_HITACHI_AC344 || DECODE_HITACHI_AC264) /// Decode the supplied Hitachi A/C message. /// Status: STABLE / Expected to work. /// @param[in,out] results Ptr to the data to decode & where to store the result @@ -835,7 +845,7 @@ String IRHitachiAc1::toString(void) const { /// raw data. Typically/Defaults to kStartOffset. /// @param[in] nbits The number of data bits to expect. /// Typically kHitachiAcBits, kHitachiAc1Bits, kHitachiAc2Bits, -/// kHitachiAc344Bits +/// kHitachiAc344Bits, kHitachiAc264Bits /// @param[in] strict Flag indicating if we should perform strict matching. /// @param[in] MSBfirst Is the data per byte stored in MSB First (true) or /// LSB First order(false)? @@ -843,6 +853,7 @@ String IRHitachiAc1::toString(void) const { /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417 /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453 /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1729 bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict, const bool MSBfirst) { @@ -853,6 +864,7 @@ bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset, case kHitachiAcBits: case kHitachiAc1Bits: case kHitachiAc2Bits: + case kHitachiAc264Bits: case kHitachiAc344Bits: break; // Okay to continue. default: @@ -879,16 +891,20 @@ bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset, // Compliance if (strict) { - if (nbits / 8 == kHitachiAcStateLength && - !IRHitachiAc::validChecksum(results->state, kHitachiAcStateLength)) - return false; - if (nbits / 8 == kHitachiAc1StateLength && - !IRHitachiAc1::validChecksum(results->state, kHitachiAc1StateLength)) - return false; - if (nbits / 8 == kHitachiAc344StateLength && - !IRHitachiAc3::hasInvertedStates(results->state, - kHitachiAc344StateLength)) - return false; + const uint16_t nbytes = nbits / 8; + switch (nbytes) { + case kHitachiAcStateLength: + if (!IRHitachiAc::validChecksum(results->state, nbytes)) return false; + break; + case kHitachiAc1StateLength: + if (!IRHitachiAc1::validChecksum(results->state, nbytes)) return false; + break; + case kHitachiAc264StateLength: + case kHitachiAc344StateLength: + if (!IRHitachiAc3::hasInvertedStates(results->state, nbytes)) + return false; + break; + } } // Success @@ -899,6 +915,9 @@ bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset, case kHitachiAc2Bits: results->decode_type = decode_type_t::HITACHI_AC2; break; + case kHitachiAc264Bits: + results->decode_type = decode_type_t::HITACHI_AC264; + break; case kHitachiAc344Bits: results->decode_type = decode_type_t::HITACHI_AC344; break; @@ -913,7 +932,7 @@ bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset, return true; } #endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || - // DECODE_HITACHI_AC344) + // DECODE_HITACHI_AC344 || DECODE_HITACHI_AC264) #if SEND_HITACHI_AC424 /// Send a Hitachi 53-byte/424-bit A/C formatted message. (HITACHI_AC424) @@ -1008,6 +1027,7 @@ void IRHitachiAc424::stateReset(void) { _.raw[3] = 0x40; _.raw[5] = 0xFF; _.raw[7] = 0xCC; + _.raw[27] = 0xE1; _.raw[33] = 0x80; _.raw[35] = 0x03; _.raw[37] = 0x01; @@ -1055,15 +1075,13 @@ void IRHitachiAc424::send(const uint16_t repeat) { /// Get the value of the current power setting. /// @return true, the setting is on. false, the setting is off. -bool IRHitachiAc424::getPower(void) const { - return _.Power == kHitachiAc424PowerOn; -} +bool IRHitachiAc424::getPower(void) const { return _.Power; } /// Change the power setting. /// @param[in] on true, the setting is on. false, the setting is off. void IRHitachiAc424::setPower(const bool on) { + _.Power = on; setButton(kHitachiAc424ButtonPowerMode); - _.Power = (on ? kHitachiAc424PowerOn : kHitachiAc424PowerOff); } /// Change the power setting to On. @@ -1203,7 +1221,7 @@ uint8_t IRHitachiAc424::convertMode(const stdAc::opmode_t mode) { /// Convert a stdAc::fanspeed_t enum into it's native speed. /// @param[in] speed The enum to be converted. /// @return The native equivalent of the enum. -uint8_t IRHitachiAc424::convertFan(const stdAc::fanspeed_t speed) { +uint8_t IRHitachiAc424::convertFan(const stdAc::fanspeed_t speed) const { switch (speed) { case stdAc::fanspeed_t::kMin: return kHitachiAc424FanMin; case stdAc::fanspeed_t::kLow: return kHitachiAc424FanLow; @@ -1230,7 +1248,7 @@ stdAc::opmode_t IRHitachiAc424::toCommonMode(const uint8_t mode) { /// Convert a native fan speed into its stdAc equivalent. /// @param[in] speed The native setting to be converted. /// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRHitachiAc424::toCommonFanSpeed(const uint8_t speed) { +stdAc::fanspeed_t IRHitachiAc424::toCommonFanSpeed(const uint8_t speed) const { switch (speed) { case kHitachiAc424FanMax: return stdAc::fanspeed_t::kMax; case kHitachiAc424FanHigh: return stdAc::fanspeed_t::kHigh; @@ -1244,7 +1262,7 @@ stdAc::fanspeed_t IRHitachiAc424::toCommonFanSpeed(const uint8_t speed) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRHitachiAc424::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::HITACHI_AC424; result.model = -1; // No models used. result.power = getPower(); @@ -1578,3 +1596,401 @@ String IRHitachiAc344::toString(void) const { result += ')'; return result; } + + +#if SEND_HITACHI_AC264 +/// Send a Hitachi 33-byte/264-bit A/C message (HITACHI_AC264) +/// Basically the same as sendHitachiAC() except different size. +/// Status: STABLE / Reported as working. +/// @param[in] data An array of bytes containing the IR command. +/// @param[in] nbytes Nr. of bytes of data in the array. +/// @param[in] repeat Nr. of times the message is to be repeated. (Default = 0). +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1729 +void IRsend::sendHitachiAc264(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kHitachiAc264StateLength) + return; // Not enough bytes to send a proper message. + sendHitachiAC(data, nbytes, repeat); +} +#endif // SEND_HITACHI_AC264 + +// Class constructor for handling detailed Hitachi_AC344 43 byte A/C messages. +/// @param[in] pin GPIO to be used when sending. +/// @param[in] inverted Is the output signal to be inverted? +/// @param[in] use_modulation Is frequency modulation to be used? +IRHitachiAc264::IRHitachiAc264(const uint16_t pin, const bool inverted, + const bool use_modulation) + : IRHitachiAc424(pin, inverted, use_modulation) { stateReset(); } + +/// Reset the internal state to auto fan, cooling, 23° Celsius +void IRHitachiAc264::stateReset(void) { + IRHitachiAc424::stateReset(); + _.raw[9] = 0x92; + _.raw[27] = 0xC1; +} + +#if SEND_HITACHI_AC264 +/// Create and send the IR message to the A/C. +/// @param[in] repeat Nr. of times to repeat the message. +void IRHitachiAc264::send(const uint16_t repeat) { + _irsend.sendHitachiAc264(getRaw(), kHitachiAc264StateLength, repeat); +} +#endif // SEND_HITACHI_AC264 + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +/// @param[in] length Size (in bytes) of the code for this protocol. +void IRHitachiAc264::setRaw(const uint8_t new_code[], const uint16_t length) { + memcpy(_.raw, new_code, std::min(length, kHitachiAc264StateLength)); +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRHitachiAc264::setFan(const uint8_t speed) { + switch (speed) { + case kHitachiAc264FanMin: + case kHitachiAc264FanMedium: + case kHitachiAc264FanHigh: + case kHitachiAc264FanAuto: + _.Fan = speed; + break; + default: + setFan(kHitachiAc264FanAuto); + } +} + +/// Convert a stdAc::fanspeed_t enum into it's native speed. +/// @param[in] speed The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRHitachiAc264::convertFan(const stdAc::fanspeed_t speed) const { + switch (speed) { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: return kHitachiAc264FanMin; + case stdAc::fanspeed_t::kMedium: return kHitachiAc264FanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kHitachiAc424FanHigh; + default: return kHitachiAc424FanAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] speed The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRHitachiAc264::toCommonFanSpeed(const uint8_t speed) const { + switch (speed) { + case kHitachiAc264FanHigh: return stdAc::fanspeed_t::kHigh; + case kHitachiAc264FanMedium: return stdAc::fanspeed_t::kMedium; + case kHitachiAc264FanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRHitachiAc264::toCommon(void) const { + stdAc::state_t result = IRHitachiAc424::toCommon(); + result.protocol = decode_type_t::HITACHI_AC264; + result.swingv = stdAc::swingv_t::kOff; + return result; +} + +/// Convert the internal state into a human readable string. +/// @return A string containing the settings in human-readable form. +String IRHitachiAc264::toString(void) const { + String result; + result.reserve(120); // Reserve some heap for the string to reduce fragging. + result += _toString(); + return result; +} + +#if DECODE_HITACHI_AC264 +// For Decoding HITACHI_AC264, see `decodeHitachiAC` +#endif // DECODE_HITACHI_AC264 + + +#if SEND_HITACHI_AC296 +/// Send a HitachiAc 37-byte/296-bit A/C message (HITACHI_AC296) +/// Status: STABLE / Working on a real device. +/// @param[in] data containing the IR command. +/// @param[in] nbytes Nr. of bytes to send. usually kHitachiAc296StateLength +/// @param[in] repeat Nr. of times the message is to be repeated. +void IRsend::sendHitachiAc296(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kHitachiAc296StateLength) + return; // Not enough bytes to send a proper message. + sendHitachiAC(data, nbytes, repeat); +} +#endif // SEND_HITACHIAC296 + +// Class constructor for handling detailed Hitachi_AC296 37 byte A/C messages. +/// @param[in] pin GPIO to be used when sending. +/// @param[in] inverted Is the output signal to be inverted? +/// @param[in] use_modulation Is frequency modulation to be used? +IRHitachiAc296::IRHitachiAc296(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +/// Reset the internal state to auto fan, heating, & 24° Celsius +void IRHitachiAc296::stateReset(void) { + // Header + _.raw[0] = 0x01; + _.raw[1] = 0x10; + _.raw[2] = 0x00; + + // Every next byte is a parity byte + _.raw[3] = 0x40; + _.raw[5] = 0xFF; + _.raw[7] = 0xCC; + _.raw[9] = 0x92; + _.raw[11] = 0x43; + // 13-14 is Temperature and parity + _.raw[15] = 0x00; + _.raw[17] = 0x00; // Off timer LSB + _.raw[19] = 0x00; // Off timer cont + _.raw[21] = 0x00; // On timer LSB + _.raw[23] = 0x00; // On timer cont + // 25-26 is Mode and fan + _.raw[27] = 0xF1; // Power on + _.raw[29] = 0x00; + _.raw[31] = 0x00; + _.raw[33] = 0x00; + _.raw[35] = 0x03; // Humidity + + setTemp(24); + setMode(kHitachiAc296Heat); + setFan(kHitachiAc296FanAuto); + + setInvertedStates(); +} + +/// Update the internal consistency check for the protocol. +void IRHitachiAc296::setInvertedStates(void) { + invertBytePairs(_.raw + 3, kHitachiAc296StateLength - 3); +} + +/// Check if every second byte of the state, after the fixed header +/// is inverted to the previous byte. +/// @param[in] state The state array to be checked. +/// @param[in] length The size of the state array. +/// @note This is this protocols integrity check. +bool IRHitachiAc296::hasInvertedStates(const uint8_t state[], + const uint16_t length) { + return IRHitachiAc3::hasInvertedStates(state, length); +} + +/// Set up hardware to be able to send a message. +void IRHitachiAc296::begin(void) { _irsend.begin(); } + +#if SEND_HITACHI_AC296 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRHitachiAc296::send(const uint16_t repeat) { + _irsend.sendHitachiAc296(getRaw(), kHitachiAc296StateLength, repeat); +} +#endif // SEND_HITACHI_AC296 + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc296::getPower(void) const { return _.Power; } + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc296::setPower(const bool on) { _.Power = on; } + +/// Change the power setting to On. +void IRHitachiAc296::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRHitachiAc296::off(void) { setPower(false); } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRHitachiAc296::getMode(void) const { return _.Mode; } + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRHitachiAc296::setMode(const uint8_t mode) { + switch (mode) { + case kHitachiAc296Heat: + case kHitachiAc296Cool: + case kHitachiAc296Dehumidify: + case kHitachiAc296AutoDehumidifying: + case kHitachiAc296Auto: + _.Mode = mode; + setTemp(getTemp()); // Reset the temp to handle "Auto"'s special temp. + break; + default: + setMode(kHitachiAc296Auto); + } +} + +/// Convert a stdAc::opmode_t enum into its native mode. +/// @param[in] mode The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRHitachiAc296::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kHitachiAc296Cool; + case stdAc::opmode_t::kHeat: return kHitachiAc296Heat; + case stdAc::opmode_t::kDry: return kHitachiAc296Dehumidify; + default: return kHitachiAc296Auto; + } +} + +/// Convert a native mode into its stdAc equivalent. +/// @param[in] mode The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::opmode_t IRHitachiAc296::toCommonMode(const uint8_t mode) { + switch (mode) { + case kHitachiAc296DryCool: + case kHitachiAc296Cool: return stdAc::opmode_t::kCool; + case kHitachiAc296Heat: return stdAc::opmode_t::kHeat; + case kHitachiAc296AutoDehumidifying: + case kHitachiAc296Dehumidify: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRHitachiAc296::getTemp(void) const { return _.Temp; } + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +void IRHitachiAc296::setTemp(const uint8_t celsius) { + uint8_t temp = celsius; + if (getMode() == kHitachiAc296Auto) { // Special temp for auto mode + temp = kHitachiAc296TempAuto; + } else { // Normal temp setting. + temp = std::min(temp, kHitachiAc296MaxTemp); + temp = std::max(temp, kHitachiAc296MinTemp); + } + _.Temp = temp; +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRHitachiAc296::getFan(void) const { return _.Fan; } + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRHitachiAc296::setFan(const uint8_t speed) { + uint8_t newSpeed = std::max(speed, kHitachiAc296FanSilent); + _.Fan = std::min(newSpeed, kHitachiAc296FanAuto); +} + +/// Convert a stdAc::fanspeed_t enum into it's native speed. +/// @param[in] speed The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRHitachiAc296::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kHitachiAc296FanSilent; + case stdAc::fanspeed_t::kLow: return kHitachiAc296FanLow; + case stdAc::fanspeed_t::kMedium: return kHitachiAc296FanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kHitachiAc296FanHigh; + default: return kHitachiAc296FanAuto; + } +} + +/// Convert a native fan speed into its stdAc equivalent. +/// @param[in] speed The native setting to be converted. +/// @return The stdAc equivalent of the native setting. +stdAc::fanspeed_t IRHitachiAc296::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kHitachiAc296FanHigh: return stdAc::fanspeed_t::kHigh; + case kHitachiAc296FanMedium: return stdAc::fanspeed_t::kMedium; + case kHitachiAc296FanLow: return stdAc::fanspeed_t::kLow; + case kHitachiAc296FanSilent: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kAuto; + } +} + +/// Get a PTR to the internal state/code for this protocol. +/// @return PTR to a code for this protocol based on the current internal state. +uint8_t *IRHitachiAc296::getRaw(void) { + setInvertedStates(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +/// @param[in] length Size (in bytes) of the code for this protocol. +void IRHitachiAc296::setRaw(const uint8_t new_code[], const uint16_t length) { + memcpy(_.raw, new_code, std::min(length, kHitachiAc296StateLength)); +} + + +/// Convert the current internal state into its stdAc::state_t equivalent. +/// @return The stdAc equivalent of the native settings. +stdAc::state_t IRHitachiAc296::toCommon(void) const { + stdAc::state_t result{}; + result.protocol = decode_type_t::HITACHI_AC296; + result.model = -1; // No models used. + result.power = getPower(); + result.mode = toCommonMode(_.Mode); + result.celsius = true; + result.degrees = _.Temp; + result.fanspeed = toCommonFanSpeed(_.Fan); + result.quiet = _.Fan == kHitachiAc296FanSilent; + // Not supported. + result.swingv = stdAc::swingv_t::kOff; + result.swingh = stdAc::swingh_t::kOff; + 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 current internal state into a human readable string. +/// @return A human readable string. +String IRHitachiAc296::toString(void) const { + String result = ""; + result.reserve(70); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(_.Power, kPowerStr, false); + result += addModeToString(_.Mode, kHitachiAc296Auto, kHitachiAc296Cool, + kHitachiAc296Heat, kHitachiAc1Dry, + kHitachiAc296Auto); + result += addTempToString(getTemp()); + result += addFanToString(_.Fan, kHitachiAc296FanHigh, kHitachiAc296FanLow, + kHitachiAc296FanAuto, kHitachiAc296FanSilent, + kHitachiAc296FanMedium); + return result; +} + +#if DECODE_HITACHI_AC296 +/// Decode the supplied Hitachi 37-byte A/C message. +/// Status: STABLE / Working on a real device. +/// @param[in,out] results Ptr to the data to decode & where to store the result +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return True if it can decode it, false if it can't. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1757 +bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kHitachiAcHdrMark, kHitachiAcHdrSpace, + kHitachiAcBitMark, kHitachiAcOneSpace, + kHitachiAcBitMark, kHitachiAcZeroSpace, + kHitachiAcBitMark, kHitachiAcMinGap, true, + kUseDefTol, 0, false)) return false; + + // Compliance + if (strict && !IRHitachiAc296::hasInvertedStates(results->state, nbits / 8)) + return false; + + // Success + results->decode_type = decode_type_t::HITACHI_AC296; + results->bits = nbits; + return true; +} +#endif // DECODE_HITACHI_AC296 diff --git a/lib/IRremoteESP8266/src/ir_Hitachi.h b/lib/IRremoteESP8266/src/ir_Hitachi.h index 32167596f..413ab75e8 100644 --- a/lib/IRremoteESP8266/src/ir_Hitachi.h +++ b/lib/IRremoteESP8266/src/ir_Hitachi.h @@ -7,6 +7,8 @@ /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1056 /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1060 /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1729 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1757 // Supports: // Brand: Hitachi, Model: RAS-35THA6 remote @@ -19,6 +21,10 @@ // Brand: Hitachi, Model: R-LT0541-HTA/Y.K.1.1-1 V2.3 remote (HITACHI_AC1) // Brand: Hitachi, Model: RAS-22NK A/C (HITACHI_AC344) // Brand: Hitachi, Model: RF11T1 remote (HITACHI_AC344) +// Brand: Hitachi, Model: RAR-2P2 remote (HITACHI_AC264) +// Brand: Hitachi, Model: RAK-25NH5 A/C (HITACHI_AC264) +// Brand: Hitachi, Model: RAR-3U3 remote (HITACHI_AC296) +// Brand: Hitachi, Model: RAS-70YHA3 A/C (HITACHI_AC296) #ifndef IR_HITACHI_H_ #define IR_HITACHI_H_ @@ -102,7 +108,9 @@ union Hitachi424Protocol{ // Byte 26 uint8_t :8; // Byte 27 - uint8_t Power :8; + uint8_t :4; + uint8_t Power :1; + uint8_t :3; // Byte 28~34 uint8_t pad2[7]; // Byte 35 @@ -160,9 +168,6 @@ const uint8_t kHitachiAc344FanHigh = kHitachiAc424FanHigh; const uint8_t kHitachiAc344FanAuto = kHitachiAc424FanAuto; const uint8_t kHitachiAc344FanMax = kHitachiAc424FanMax; -const uint8_t kHitachiAc424PowerOn = 0xF1; -const uint8_t kHitachiAc424PowerOff = 0xE1; - const uint8_t kHitachiAc344SwingHAuto = 0; // 0b000 const uint8_t kHitachiAc344SwingHRightMax = 1; // 0b001 const uint8_t kHitachiAc344SwingHRight = 2; // 0b010 @@ -240,6 +245,127 @@ const uint8_t kHitachiAc1Sleep4 = 0b100; const uint8_t kHitachiAc1ChecksumStartByte = 5; +/// Native representation of a Hitachi 164-bit A/C message. +union HitachiAC264Protocol{ + uint8_t raw[kHitachiAc264StateLength]; ///< The state in native code. + struct { + // Bytes 0~10 + uint8_t pad0[11]; + // Byte 11 + uint8_t Button :8; + // Byte 12 + uint8_t :8; + // Byte 13 + uint8_t :2; + uint8_t Temp :6; + // Byte 14 + uint8_t :8; + // Bytes 14~24 + uint8_t pad1[10]; + // Byte 25 + uint8_t Mode :4; + uint8_t Fan :4; + // Byte 26 + uint8_t :8; + // Byte 27 + uint8_t :4; + uint8_t Power :1; + uint8_t :3; + // Byte 28 + uint8_t :8; + // Bytes 29~32 + uint8_t pad2[4]; + }; +}; + +// HitachiAc264 +const uint8_t kHitachiAc264ButtonPowerMode = kHitachiAc424ButtonPowerMode; +const uint8_t kHitachiAc264ButtonFan = kHitachiAc424ButtonFan; +const uint8_t kHitachiAc264ButtonTempDown = kHitachiAc424ButtonTempDown; +const uint8_t kHitachiAc264ButtonTempUp = kHitachiAc424ButtonTempUp; +const uint8_t kHitachiAc264ButtonSwingV = kHitachiAc424ButtonSwingV; +const uint8_t kHitachiAc264MinTemp = kHitachiAc424MinTemp; // 16C +const uint8_t kHitachiAc264MaxTemp = kHitachiAc424MaxTemp; // 32C +const uint8_t kHitachiAc264Fan = kHitachiAc424Fan; +const uint8_t kHitachiAc264Cool = kHitachiAc424Cool; +const uint8_t kHitachiAc264Dry = kHitachiAc424Dry; +const uint8_t kHitachiAc264Heat = kHitachiAc424Heat; +const uint8_t kHitachiAc264FanMin = kHitachiAc424FanMin; +const uint8_t kHitachiAc264FanLow = kHitachiAc424FanMin; +const uint8_t kHitachiAc264FanMedium = kHitachiAc424FanMedium; +const uint8_t kHitachiAc264FanHigh = kHitachiAc424FanHigh; +const uint8_t kHitachiAc264FanAuto = kHitachiAc424FanAuto; + +// HitachiAc296 +union HitachiAC296Protocol{ + uint8_t raw[kHitachiAc296StateLength]; + struct { + // Byte 0~12 + uint8_t pad0[13]; + // Byte 13 + uint8_t :2; + uint8_t Temp :5; // LSB + uint8_t :1; + uint8_t :8; + // Byte 15~16 + uint8_t :8; + uint8_t :8; + // Byte 17~24 + uint8_t OffTimerLow :8; // LSB + uint8_t /* Parity */ :8; + uint8_t OffTimerHigh :8; + uint8_t /* Parity */ :8; + uint8_t OnTimerLow :8; // LSB + uint8_t /* Parity */ :8; + uint8_t OnTimerHigh :4; + uint8_t OffTimerActive :1; + uint8_t OnTimerActive :1; + uint8_t :2; + uint8_t /* Parity */ :8; + // Byte 25~26 + uint8_t Mode :4; + uint8_t Fan :3; + uint8_t :1; + uint8_t :8; + // Byte 27~28 + uint8_t :4; + uint8_t Power :1; + uint8_t :2; + uint8_t TimerActive :1; + uint8_t :8; + // Byte 29~34 + uint8_t pad1[6]; + // Byte 35~36 + uint8_t :4; + uint8_t Humidity :4; // LSB + uint8_t :8; + }; +}; + +// Mode & Fan +const uint8_t kHitachiAc296Cool = 0b0011; +const uint8_t kHitachiAc296DryCool = 0b0100; +const uint8_t kHitachiAc296Dehumidify = 0b0101; +const uint8_t kHitachiAc296Heat = 0b0110; +const uint8_t kHitachiAc296Auto = 0b0111; +const uint8_t kHitachiAc296AutoDehumidifying = 0b1001; +const uint8_t kHitachiAc296QuickLaundry = 0b1010; +const uint8_t kHitachiAc296CondensationControl = 0b1100; + +const uint8_t kHitachiAc296FanSilent = 0b001; +const uint8_t kHitachiAc296FanLow = 0b010; +const uint8_t kHitachiAc296FanMedium = 0b011; +const uint8_t kHitachiAc296FanHigh = 0b100; +const uint8_t kHitachiAc296FanAuto = 0b101; + +const uint8_t kHitachiAc296TempAuto = 1; // Special value for "Auto" op mode. +const uint8_t kHitachiAc296MinTemp = 16; +const uint8_t kHitachiAc296MaxTemp = 31; // Max value you can store in 5 bits. + +const uint8_t kHitachiAc296PowerOn = 1; +const uint8_t kHitachiAc296PowerOff = 0; + + // Classes /// Class for handling detailed Hitachi 224-bit A/C messages. /// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/HitachiHeatpumpIR.cpp @@ -369,6 +495,7 @@ class IRHitachiAc1 { /// Class for handling detailed Hitachi 53-byte/424-bit A/C messages. class IRHitachiAc424 { + friend class IRHitachiAc264; friend class IRHitachiAc344; public: explicit IRHitachiAc424(const uint16_t pin, const bool inverted = false, @@ -389,7 +516,7 @@ class IRHitachiAc424 { bool getPower(void) const; void setTemp(const uint8_t temp, bool setPrevious = true); uint8_t getTemp(void) const; - void setFan(const uint8_t speed); + virtual void setFan(const uint8_t speed); uint8_t getFan(void) const; uint8_t getButton(void) const; void setButton(const uint8_t button); @@ -401,9 +528,9 @@ class IRHitachiAc424 { virtual void setRaw(const uint8_t new_code[], const uint16_t length = kHitachiAc424StateLength); static uint8_t convertMode(const stdAc::opmode_t mode); - static uint8_t convertFan(const stdAc::fanspeed_t speed); + virtual uint8_t convertFan(const stdAc::fanspeed_t speed) const; static stdAc::opmode_t toCommonMode(const uint8_t mode); - static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + virtual stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed) const; virtual stdAc::state_t toCommon(void) const; virtual String toString(void) const; #ifndef UNIT_TEST @@ -475,4 +602,66 @@ class IRHitachiAc344: public IRHitachiAc424 { static stdAc::swingh_t toCommonSwingH(const uint8_t pos); String toString(void) const override; }; + +/// Class for handling detailed Hitachi 264-bit A/C messages. +class IRHitachiAc264: public IRHitachiAc424 { + public: + explicit IRHitachiAc264(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + void stateReset(void) override; + void setRaw(const uint8_t new_code[], + const uint16_t length = kHitachiAc264StateLength) override; + void setFan(const uint8_t speed) override; + uint8_t convertFan(const stdAc::fanspeed_t speed) const override; + stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed) const override; + stdAc::state_t toCommon(void) const override; +#if SEND_HITACHI_AC264 + void send(const uint16_t repeat = kHitachiAcDefaultRepeat) override; +#endif // SEND_HITACHI_AC264 + String toString(void) const override; +}; + +class IRHitachiAc296 { + public: + explicit IRHitachiAc296(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + void stateReset(void); + +#if SEND_HITACHI_AC296 + void send(const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC296 + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void) const; + void setTemp(const uint8_t temp); + uint8_t getTemp(void) const; + void setFan(const uint8_t speed); + uint8_t getFan(void) const; + void setMode(const uint8_t mode); + uint8_t getMode(void) const; + static bool hasInvertedStates(const uint8_t state[], const uint16_t length); + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kHitachiAc296StateLength); + 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) const; + String toString(void) const; +#ifndef UNIT_TEST + + private: + IRsend _irsend; ///< Instance of the IR send class +#else // UNIT_TEST + /// @cond IGNORE + IRsendTest _irsend; ///< Instance of the testing IR send class + /// @endcond +#endif // UNIT_TEST + + HitachiAC296Protocol _; + void setInvertedStates(void); +}; #endif // IR_HITACHI_H_ diff --git a/lib/IRremoteESP8266/src/ir_Kelon.cpp b/lib/IRremoteESP8266/src/ir_Kelon.cpp index 39b61744e..bb8f16093 100644 --- a/lib/IRremoteESP8266/src/ir_Kelon.cpp +++ b/lib/IRremoteESP8266/src/ir_Kelon.cpp @@ -1,7 +1,8 @@ // Copyright 2021 Davide Depau +// Copyright 2022 David Conran /// @file -/// @brief Support for Kelan AC protocol. +/// @brief Support for Kelon AC protocols. /// Both sending and decoding should be functional for models of series /// KELON ON/OFF 9000-12000. /// All features of the standard remote are implemented. @@ -12,6 +13,7 @@ /// - Fahrenheit. #include +#include #include "ir_Kelon.h" @@ -39,9 +41,13 @@ const uint16_t kKelonZeroSpace = 600; const uint32_t kKelonGap = 2 * kDefaultMessageGap; const uint16_t kKelonFreq = 38000; -#if SEND_KELON +const uint32_t kKelon168FooterSpace = 8000; +const uint16_t kKelon168Section1Size = 6; +const uint16_t kKelon168Section2Size = 8; +const uint16_t kKelon168Section3Size = 7; -/// Send a Kelon message. +#if SEND_KELON +/// Send a Kelon 48-bit message. /// Status: STABLE / Working. /// @param[in] data The data to be transmitted. /// @param[in] nbits Nr. of bits of data to be sent. @@ -53,13 +59,12 @@ void IRsend::sendKelon(const uint64_t data, const uint16_t nbits, kKelonBitMark, kKelonZeroSpace, kKelonBitMark, kKelonGap, data, nbits, kKelonFreq, false, // LSB First. - repeat, 50); + repeat, kDutyDefault); } - #endif // SEND_KELON #if DECODE_KELON -/// Decode the supplied Kelon message. +/// Decode the supplied Kelon 48-bit message. /// Status: STABLE / Working. /// @param[in,out] results Ptr to the data to decode & where to store the result /// @param[in] offset The starting index to use when attempting to decode the @@ -67,27 +72,24 @@ void IRsend::sendKelon(const uint64_t data, const uint16_t nbits, /// @param[in] nbits The number of data bits to expect. /// @param[in] strict Flag indicating if we should perform strict matching. /// @return True if it can decode it, false if it can't. - bool IRrecv::decodeKelon(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { - if (strict && nbits != kKelonBits) { - return false; - } - if (!matchGeneric(results->rawbuf + offset, results->state, + if (strict && nbits != kKelonBits) return false; + + if (!matchGeneric(results->rawbuf + offset, &(results->value), results->rawlen - offset, nbits, kKelonHdrMark, kKelonHdrSpace, kKelonBitMark, kKelonOneSpace, kKelonBitMark, kKelonZeroSpace, - kKelonBitMark, 0, false, - _tolerance, 0, false)) { - return false; - } + kKelonBitMark, kKelonGap, true, + _tolerance, 0, false)) return false; results->decode_type = decode_type_t::KELON; + results->address = 0; + results->command = 0; results->bits = nbits; return true; } - #endif // DECODE_KELON /// Class constructor @@ -151,21 +153,15 @@ void IRKelonAc::ensurePower(bool on) { #endif // SEND_KELON /// Set up hardware to be able to send a message. -void IRKelonAc::begin() { - _irsend.begin(); -} +void IRKelonAc::begin() { _irsend.begin(); } /// Request toggling power - will be reset to false after sending /// @param[in] toggle Whether to toggle the power state -void IRKelonAc::setTogglePower(const bool toggle) { - _.PowerToggle = toggle; -} +void IRKelonAc::setTogglePower(const bool toggle) { _.PowerToggle = toggle; } /// Get whether toggling power will be requested /// @return The power toggle state -bool IRKelonAc::getTogglePower() const { - return _.PowerToggle; -} +bool IRKelonAc::getTogglePower() const { return _.PowerToggle; } /// Set the temperature setting. /// @param[in] degrees The temperature in degrees celsius. @@ -178,9 +174,7 @@ void IRKelonAc::setTemp(const uint8_t degrees) { /// Get the current temperature setting. /// @return Get current setting for temp. in degrees celsius. -uint8_t IRKelonAc::getTemp() const { - return _.Temperature + kKelonMinTemp; -} +uint8_t IRKelonAc::getTemp() const { return _.Temperature + kKelonMinTemp; } /// Set the speed of the fan. /// @param[in] speed 0 is auto, 1-5 is the speed @@ -207,11 +201,10 @@ void IRKelonAc::setDryGrade(const int8_t grade) { // Two's complement is clearly too bleeding edge for this manufacturer uint8_t outval; - if (drygrade < 0) { + if (drygrade < 0) outval = 0b100 | (-drygrade & 0b011); - } else { + else outval = drygrade & 0b011; - } _.DehumidifierGrade = outval; } @@ -260,9 +253,7 @@ void IRKelonAc::setMode(const uint8_t mode) { /// Get the current operation mode setting. /// @return The current operation mode. -uint8_t IRKelonAc::getMode() const { - return _.Mode; -} +uint8_t IRKelonAc::getMode() const { return _.Mode; } /// Request toggling the vertical swing - will be reset to false after sending /// @param[in] toggle If true, the swing mode will be toggled when sent. @@ -272,21 +263,15 @@ void IRKelonAc::setToggleSwingVertical(const bool toggle) { /// Get whether the swing mode is set to be toggled /// @return Whether the toggle bit is set -bool IRKelonAc::getToggleSwingVertical() const { - return _.SwingVToggle; -} +bool IRKelonAc::getToggleSwingVertical() const { return _.SwingVToggle; } /// Control the current sleep (quiet) setting. /// @param[in] on The desired setting. -void IRKelonAc::setSleep(const bool on) { - _.SleepEnabled = on; -} +void IRKelonAc::setSleep(const bool on) { _.SleepEnabled = on; } /// Is the sleep setting on? /// @return The current value. -bool IRKelonAc::getSleep() const { - return _.SleepEnabled; -} +bool IRKelonAc::getSleep() const { return _.SleepEnabled; } /// Control the current super cool mode setting. /// @param[in] on The desired setting. @@ -305,9 +290,7 @@ void IRKelonAc::setSupercool(const bool on) { /// Is the super cool mode setting on? /// @return The current value. -bool IRKelonAc::getSupercool() const { - return _.SuperCoolEnabled1; -} +bool IRKelonAc::getSupercool() const { return _.SuperCoolEnabled1; } /// Set the timer time and enable it. Timer is an off timer if the unit is on, /// it is an on timer if the unit is off. @@ -334,54 +317,39 @@ void IRKelonAc::setTimer(uint16_t mins) { /// later disabled. /// @return The timer set minutes uint16_t IRKelonAc::getTimer() const { - if (_.TimerHours >= 10) { + if (_.TimerHours >= 10) return ((uint16_t) ((_.TimerHours << 1) | _.TimerHalfHour) - 10) * 60; - } return (((uint16_t) _.TimerHours) * 60) + (_.TimerHalfHour ? 30 : 0); } /// Enable or disable the timer. Note that in order to enable the timer the /// minutes must be set with setTimer(). /// @param[in] on Whether to enable or disable the timer -void IRKelonAc::setTimerEnabled(bool on) { - _.TimerEnabled = on; -} +void IRKelonAc::setTimerEnabled(bool on) { _.TimerEnabled = on; } /// Get the current timer status /// @return Whether the timer is enabled. -bool IRKelonAc::getTimerEnabled() const { - return _.TimerEnabled; -} - +bool IRKelonAc::getTimerEnabled() const { return _.TimerEnabled; } /// Get the raw state of the object, suitable to be sent with the appropriate /// IRsend object method. /// @return A PTR to the internal state. -uint64_t IRKelonAc::getRaw() const { - return _.raw; -} +uint64_t IRKelonAc::getRaw() const { return _.raw; } /// Set the raw state of the object. /// @param[in] new_code The raw state from the native IR message. -void IRKelonAc::setRaw(const uint64_t new_code) { - _.raw = new_code; -} +void IRKelonAc::setRaw(const uint64_t new_code) { _.raw = new_code; } /// Convert a standard A/C mode (stdAc::opmode_t) into it a native mode. /// @param[in] mode A stdAc::opmode_t operation mode. /// @return The native mode equivalent. uint8_t IRKelonAc::convertMode(const stdAc::opmode_t mode) { switch (mode) { - case stdAc::opmode_t::kCool: - return kKelonModeCool; - case stdAc::opmode_t::kHeat: - return kKelonModeHeat; - case stdAc::opmode_t::kDry: - return kKelonModeDry; - case stdAc::opmode_t::kFan: - return kKelonModeFan; - default: - return kKelonModeSmart; + case stdAc::opmode_t::kCool: return kKelonModeCool; + case stdAc::opmode_t::kHeat: return kKelonModeHeat; + case stdAc::opmode_t::kDry: return kKelonModeDry; + case stdAc::opmode_t::kFan: return kKelonModeFan; + default: return kKelonModeSmart; // aka Auto. } } @@ -391,15 +359,11 @@ uint8_t IRKelonAc::convertMode(const stdAc::opmode_t mode) { uint8_t IRKelonAc::convertFan(stdAc::fanspeed_t fan) { switch (fan) { case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: - return kKelonFanMin; - case stdAc::fanspeed_t::kMedium: - return kKelonFanMedium; + case stdAc::fanspeed_t::kLow: return kKelonFanMin; + case stdAc::fanspeed_t::kMedium: return kKelonFanMedium; case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: - return kKelonFanMax; - default: - return kKelonFanAuto; + case stdAc::fanspeed_t::kMax: return kKelonFanMax; + default: return kKelonFanAuto; } } @@ -408,16 +372,11 @@ uint8_t IRKelonAc::convertFan(stdAc::fanspeed_t fan) { /// @return The stdAc::opmode_t equivalent. stdAc::opmode_t IRKelonAc::toCommonMode(const uint8_t mode) { switch (mode) { - case kKelonModeCool: - return stdAc::opmode_t::kCool; - case kKelonModeHeat: - return stdAc::opmode_t::kHeat; - case kKelonModeDry: - return stdAc::opmode_t::kDry; - case kKelonModeFan: - return stdAc::opmode_t::kFan; - default: - return stdAc::opmode_t::kAuto; + case kKelonModeCool: return stdAc::opmode_t::kCool; + case kKelonModeHeat: return stdAc::opmode_t::kHeat; + case kKelonModeDry: return stdAc::opmode_t::kDry; + case kKelonModeFan: return stdAc::opmode_t::kFan; + default: return stdAc::opmode_t::kAuto; } } @@ -426,14 +385,10 @@ stdAc::opmode_t IRKelonAc::toCommonMode(const uint8_t mode) { /// @return The stdAc::fanspeed_t equivalent. stdAc::fanspeed_t IRKelonAc::toCommonFanSpeed(const uint8_t speed) { switch (speed) { - case kKelonFanMin: - return stdAc::fanspeed_t::kLow; - case kKelonFanMedium: - return stdAc::fanspeed_t::kMedium; - case kKelonFanMax: - return stdAc::fanspeed_t::kHigh; - default: - return stdAc::fanspeed_t::kAuto; + case kKelonFanMin: return stdAc::fanspeed_t::kLow; + case kKelonFanMedium: return stdAc::fanspeed_t::kMedium; + case kKelonFanMax: return stdAc::fanspeed_t::kHigh; + default: return stdAc::fanspeed_t::kAuto; } } @@ -443,21 +398,20 @@ stdAc::state_t IRKelonAc::toCommon(const stdAc::state_t *prev) const { stdAc::state_t result{}; result.protocol = decode_type_t::KELON; result.model = -1; // Unused. + // AC only supports toggling it + result.power = (prev == nullptr || prev->power) ^ _.PowerToggle; result.mode = toCommonMode(getMode()); result.celsius = true; result.degrees = getTemp(); result.fanspeed = toCommonFanSpeed(getFan()); + // AC only supports toggling it + result.swingv = stdAc::swingv_t::kAuto; + if (prev != nullptr && + (prev->swingv != stdAc::swingv_t::kAuto) ^ _.SwingVToggle) + result.swingv = stdAc::swingv_t::kOff; result.turbo = getSupercool(); result.sleep = getSleep() ? 0 : -1; // Not supported. - // N/A, AC only supports toggling it - result.power = (prev == nullptr || prev->power) ^ _.PowerToggle; - // N/A, AC only supports toggling it - result.swingv = stdAc::swingv_t::kAuto; - if (prev != nullptr && - (prev->swingv != stdAc::swingv_t::kAuto) ^ _.SwingVToggle) { - result.swingv = stdAc::swingv_t::kOff; - } result.swingh = stdAc::swingh_t::kOff; result.light = true; result.beep = true; @@ -483,20 +437,115 @@ String IRKelonAc::toString() const { result += addBoolToString(_.SleepEnabled, kSleepStr); result += addSignedIntToString(getDryGrade(), kDryStr); result += addLabeledString( - getTimerEnabled() - ? ( - getTimer() > 0 - ? minsToString(getTimer()) - : kOnStr - ) - : kOffStr, + getTimerEnabled() ? (getTimer() > 0 ? minsToString(getTimer()) : kOnStr) + : kOffStr, kTimerStr); result += addBoolToString(getSupercool(), kTurboStr); - if (getTogglePower()) { + if (getTogglePower()) result += addBoolToString(true, kPowerToggleStr); - } - if (getToggleSwingVertical()) { + if (getToggleSwingVertical()) result += addBoolToString(true, kSwingVToggleStr); - } return result; } + +#if SEND_KELON168 +/// Send a Kelon 168 bit / 21 byte message. +/// Status: BETA / Probably works. +/// @param[in] data The data to be transmitted. +/// @param[in] nbytes Nr. of bytes of data to be sent. +/// @param[in] repeat The number of times the command is to be repeated. +void IRsend::sendKelon168(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + assert(kKelon168StateLength == kKelon168Section1Size + kKelon168Section2Size + + kKelon168Section3Size); + // Enough bytes to send a proper message? + if (nbytes < kKelon168StateLength) return; + + for (uint16_t r = 0; r <= repeat; r++) { + // Section #1 (48 bits) + sendGeneric(kKelonHdrMark, kKelonHdrSpace, + kKelonBitMark, kKelonOneSpace, + kKelonBitMark, kKelonZeroSpace, + kKelonBitMark, kKelon168FooterSpace, + data, kKelon168Section1Size, kKelonFreq, false, // LSB First. + 0, // No repeats here + kDutyDefault); + // Section #2 (64 bits) + sendGeneric(0, 0, + kKelonBitMark, kKelonOneSpace, + kKelonBitMark, kKelonZeroSpace, + kKelonBitMark, kKelon168FooterSpace, + data + kKelon168Section1Size, kKelon168Section2Size, + kKelonFreq, false, // LSB First. + 0, // No repeats here + kDutyDefault); + // Section #3 (56 bits) + sendGeneric(0, 0, + kKelonBitMark, kKelonOneSpace, + kKelonBitMark, kKelonZeroSpace, + kKelonBitMark, kKelonGap, + data + kKelon168Section1Size + kKelon168Section2Size, + nbytes - (kKelon168Section1Size + kKelon168Section2Size), + kKelonFreq, false, // LSB First. + 0, // No repeats here + kDutyDefault); + } +} +#endif // SEND_KELON168 + +#if DECODE_KELON168 +/// Decode the supplied Kelon 168 bit / 21 byte message. +/// Status: BETA / Probably Working. +/// @param[in,out] results Ptr to the data to decode & where to store the result +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return True if it can decode it, false if it can't. +bool IRrecv::decodeKelon168(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kKelon168Bits) return false; + if (results->rawlen <= 2 * nbits + kHeader + kFooter * 2 - 1 + offset) + return false; // Can't possibly be a valid Kelon 168 bit message. + + uint16_t used = 0; + + used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, kKelon168Section1Size * 8, + kKelonHdrMark, kKelonHdrSpace, + kKelonBitMark, kKelonOneSpace, + kKelonBitMark, kKelonZeroSpace, + kKelonBitMark, kKelon168FooterSpace, + false, _tolerance, 0, false); + if (!used) return false; // Failed to match. + offset += used; + + used = matchGeneric(results->rawbuf + offset, + results->state + kKelon168Section1Size, + results->rawlen - offset, kKelon168Section2Size * 8, + 0, 0, + kKelonBitMark, kKelonOneSpace, + kKelonBitMark, kKelonZeroSpace, + kKelonBitMark, kKelon168FooterSpace, + false, _tolerance, 0, false); + if (!used) return false; // Failed to match. + offset += used; + + used = matchGeneric(results->rawbuf + offset, + results->state + (kKelon168Section1Size + + kKelon168Section2Size), + results->rawlen - offset, + nbits - (kKelon168Section1Size + + kKelon168Section2Size) * 8, + 0, 0, + kKelonBitMark, kKelonOneSpace, + kKelonBitMark, kKelonZeroSpace, + kKelonBitMark, kKelonGap, + true, _tolerance, 0, false); + if (!used) return false; // Failed to match. + + results->decode_type = decode_type_t::KELON168; + results->bits = nbits; + return true; +} +#endif // DECODE_KELON168 diff --git a/lib/IRremoteESP8266/src/ir_Kelon.h b/lib/IRremoteESP8266/src/ir_Kelon.h index 498650623..126669940 100644 --- a/lib/IRremoteESP8266/src/ir_Kelon.h +++ b/lib/IRremoteESP8266/src/ir_Kelon.h @@ -2,16 +2,23 @@ /// @file /// @brief Support for Kelan AC protocol. -/// Both sending and decoding should be functional for models of series KELON -/// ON/OFF 9000-12000. +/// @note Both sending and decoding should be functional for models of series +/// KELON ON/OFF 9000-12000. /// All features of the standard remote are implemented. /// /// @note Unsupported: /// - Explicit on/off due to AC unit limitations /// - Explicit swing position due to AC unit limitations /// - Fahrenheit. +/// +/// For KELON168: +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1745 + // Supports: -// Brand: Kelon, Model: ON/OFF 9000-12000 +// Brand: Kelon, Model: ON/OFF 9000-12000 (KELON) +// Brand: Kelon, Model: DG11R2-01 remote (KELON168) +// Brand: Kelon, Model: AST-09UW4RVETG00A A/C (KELON168) +// Brand: Hisense, Model: AST-09UW4RVETG00A A/C (KELON168) #ifndef IR_KELON_H_ #define IR_KELON_H_ @@ -71,84 +78,50 @@ class IRKelonAc { public: explicit IRKelonAc(uint16_t pin, bool inverted = false, bool use_modulation = true); - void stateReset(void); - #if SEND_KELON - void send(const uint16_t repeat = kNoRepeat); - /// Run the calibration to calculate uSec timing offsets for this platform. /// @return The uSec timing offset needed per modulation of the IR Led. /// @note This will produce a 65ms IR signal pulse at 38kHz. /// Only ever needs to be run once per object instantiation, if at all. int8_t calibrate(void) { return _irsend.calibrate(); } - /// Since the AC does not support actually setting the power state to a known /// value, this utility allow ensuring the AC is on or off by exploiting /// the fact that the AC, according to the user manual, will always turn on /// when setting it to "smart" or "super" mode. void ensurePower(const bool on); - - #endif + #endif // SEND_KELON void begin(void); - void setTogglePower(const bool toggle); - bool getTogglePower(void) const; - void setTemp(const uint8_t degrees); - uint8_t getTemp(void) const; - void setFan(const uint8_t speed); - uint8_t getFan(void) const; - void setDryGrade(const int8_t grade); - int8_t getDryGrade(void) const; - void setMode(const uint8_t mode); - uint8_t getMode(void) const; - void setToggleSwingVertical(const bool toggle); - bool getToggleSwingVertical(void) const; - void setSleep(const bool on); - bool getSleep(void) const; - void setSupercool(const bool on); - bool getSupercool(void) const; - void setTimer(const uint16_t mins); - uint16_t getTimer(void) const; - void setTimerEnabled(const bool on); - bool getTimerEnabled(void) const; - uint64_t getRaw(void) const; - void setRaw(const uint64_t new_code); - static uint8_t convertMode(const stdAc::opmode_t mode); - static uint8_t convertFan(const stdAc::fanspeed_t fan); - 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 = nullptr) const; - String toString(void) const; private: @@ -166,5 +139,4 @@ class IRKelonAc { uint8_t _previousTemp = kKelonMinTemp; uint8_t _previousFan = kKelonFanAuto; }; - #endif // IR_KELON_H_ diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp index 01d2e544c..c44f7c683 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.cpp +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.cpp @@ -53,6 +53,7 @@ using irutils::addLabeledString; using irutils::addModeToString; using irutils::addFanToString; using irutils::addTempToString; +using irutils::addSwingVToString; #if SEND_KELVINATOR /// Send a Kelvinator A/C message. @@ -274,16 +275,47 @@ void IRKelvinatorAC::setMode(const uint8_t mode) { } } -/// Control the current vertical swing setting. -/// @param[in] on The desired setting. -void IRKelvinatorAC::setSwingVertical(const bool on) { - _.SwingV = on; - _.VentSwing = (on || _.SwingH); +/// Set the Vertical Swing mode of the A/C. +/// @param[in] automatic Do we use the automatic setting? +/// @param[in] position The position/mode to set the vanes to. +void IRKelvinatorAC::setSwingVertical(const bool automatic, + const uint8_t position) { + _.SwingAuto = (automatic || _.SwingH); + uint8_t new_position = position; + if (!automatic) { + switch (position) { + case kKelvinatorSwingVHighest: + case kKelvinatorSwingVUpperMiddle: + case kKelvinatorSwingVMiddle: + case kKelvinatorSwingVLowerMiddle: + case kKelvinatorSwingVLowest: + break; + default: + new_position = kKelvinatorSwingVOff; + } + } else { + switch (position) { + case kKelvinatorSwingVAuto: + case kKelvinatorSwingVLowAuto: + case kKelvinatorSwingVMiddleAuto: + case kKelvinatorSwingVHighAuto: + break; + default: + new_position = kKelvinatorSwingVAuto; + } + } + _.SwingV = new_position; } -/// Is the vertical swing setting on? -/// @return The current value. -bool IRKelvinatorAC::getSwingVertical(void) const { +/// Get the Vertical Swing Automatic mode setting of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRKelvinatorAC::getSwingVerticalAuto(void) const { + return _.SwingV & 0b0001; +} + +/// Get the Vertical Swing position setting of the A/C. +/// @return The native position/mode. +uint8_t IRKelvinatorAC::getSwingVerticalPosition(void) const { return _.SwingV; } @@ -291,7 +323,7 @@ bool IRKelvinatorAC::getSwingVertical(void) const { /// @param[in] on The desired setting. void IRKelvinatorAC::setSwingHorizontal(const bool on) { _.SwingH = on; - _.VentSwing = (on || _.SwingV); + _.SwingAuto = (on || (_.SwingV & 0b0001)); } /// Is the horizontal swing setting on? @@ -378,6 +410,20 @@ uint8_t IRKelvinatorAC::convertMode(const stdAc::opmode_t mode) { } } +/// Convert a stdAc::swingv_t enum into it's native setting. +/// @param[in] swingv The enum to be converted. +/// @return The native equivalent of the enum. +uint8_t IRKelvinatorAC::convertSwingV(const stdAc::swingv_t swingv) { + switch (swingv) { + case stdAc::swingv_t::kHighest: return kKelvinatorSwingVHighest; + case stdAc::swingv_t::kHigh: return kKelvinatorSwingVHighAuto; + case stdAc::swingv_t::kMiddle: return kKelvinatorSwingVMiddle; + case stdAc::swingv_t::kLow: return kKelvinatorSwingVLowAuto; + case stdAc::swingv_t::kLowest: return kKelvinatorSwingVLowest; + default: return kKelvinatorSwingVAuto; + } +} + /// Convert a native mode to it's stdAc::opmode_t equivalent. /// @param[in] mode A native operating mode value. /// @return The stdAc::opmode_t equivalent. @@ -401,7 +447,7 @@ stdAc::fanspeed_t IRKelvinatorAC::toCommonFanSpeed(const uint8_t speed) { /// Convert the internal A/C object state to it's stdAc::state_t equivalent. /// @return A stdAc::state_t containing the current settings. stdAc::state_t IRKelvinatorAC::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::KELVINATOR; result.model = -1; // Unused. result.power = _.Power; @@ -442,7 +488,17 @@ String IRKelvinatorAC::toString(void) const { result += addBoolToString(_.IonFilter, kIonStr); result += addBoolToString(_.Light, kLightStr); result += addBoolToString(_.SwingH, kSwingHStr); - result += addBoolToString(_.SwingV, kSwingVStr); + result += addSwingVToString(_.SwingV, kKelvinatorSwingVAuto, + kKelvinatorSwingVHighest, + kKelvinatorSwingVHighAuto, + kKelvinatorSwingVUpperMiddle, + kKelvinatorSwingVMiddle, + kKelvinatorSwingVLowerMiddle, + kKelvinatorSwingVLowAuto, + kKelvinatorSwingVLowest, + kKelvinatorSwingVOff, + kKelvinatorSwingVAuto, kKelvinatorSwingVAuto, + kKelvinatorSwingVAuto); return result; } diff --git a/lib/IRremoteESP8266/src/ir_Kelvinator.h b/lib/IRremoteESP8266/src/ir_Kelvinator.h index 74371d8ef..32cb3e1fa 100644 --- a/lib/IRremoteESP8266/src/ir_Kelvinator.h +++ b/lib/IRremoteESP8266/src/ir_Kelvinator.h @@ -14,6 +14,7 @@ // Brand: Kelvinator, Model: KSV70HRC A/C // Brand: Kelvinator, Model: KSV80HRC A/C // Brand: Green, Model: YAPOF3 remote +// Brand: Gree, Model: YAP0F8 remote // Brand: Sharp, Model: YB1FA remote // Brand: Sharp, Model: A5VEY A/C @@ -39,7 +40,7 @@ union KelvinatorProtocol{ uint8_t Mode :3; uint8_t Power :1; uint8_t BasicFan :2; - uint8_t VentSwing :1; + uint8_t SwingAuto :1; uint8_t :1; // Sleep Modes 1 & 3 (1 = On, 0 = Off) // Byte 1 uint8_t Temp :4; // Degrees C. @@ -56,8 +57,7 @@ union KelvinatorProtocol{ uint8_t :2; // End of command block (B01) // (B010 marker and a gap of 20ms) // Byte 4 - uint8_t SwingV :1; - uint8_t :3; + uint8_t SwingV :4; uint8_t SwingH :1; uint8_t :3; // Byte 5~6 @@ -103,6 +103,17 @@ const uint8_t kKelvinatorMinTemp = 16; // 16C const uint8_t kKelvinatorMaxTemp = 30; // 30C const uint8_t kKelvinatorAutoTemp = 25; // 25C +const uint8_t kKelvinatorSwingVOff = 0b0000; // 0 +const uint8_t kKelvinatorSwingVAuto = 0b0001; // 1 +const uint8_t kKelvinatorSwingVHighest = 0b0010; // 2 +const uint8_t kKelvinatorSwingVUpperMiddle = 0b0011; // 3 +const uint8_t kKelvinatorSwingVMiddle = 0b0100; // 4 +const uint8_t kKelvinatorSwingVLowerMiddle = 0b0101; // 5 +const uint8_t kKelvinatorSwingVLowest = 0b0110; // 6 +const uint8_t kKelvinatorSwingVLowAuto = 0b0111; // 7 +const uint8_t kKelvinatorSwingVMiddleAuto = 0b1001; // 9 +const uint8_t kKelvinatorSwingVHighAuto = 0b1011; // 11 + // Legacy defines (Deprecated) #define KELVINATOR_MIN_TEMP kKelvinatorMinTemp #define KELVINATOR_MAX_TEMP kKelvinatorMaxTemp @@ -142,8 +153,11 @@ class IRKelvinatorAC { uint8_t getFan(void) const; void setMode(const uint8_t mode); uint8_t getMode(void) const; - void setSwingVertical(const bool on); - bool getSwingVertical(void) const; + void setSwingVertical(const bool automatic, const uint8_t position); + bool getSwingVerticalAuto(void) const; + uint8_t getSwingVerticalPosition(void) const; + static uint8_t convertSwingV(const stdAc::swingv_t swingv); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); void setSwingHorizontal(const bool on); bool getSwingHorizontal(void) const; void setQuiet(const bool on); diff --git a/lib/IRremoteESP8266/src/ir_LG.cpp b/lib/IRremoteESP8266/src/ir_LG.cpp index d817fbdb7..94cf01cd6 100644 --- a/lib/IRremoteESP8266/src/ir_LG.cpp +++ b/lib/IRremoteESP8266/src/ir_LG.cpp @@ -22,6 +22,7 @@ using irutils::addModeToString; using irutils::addModelToString; using irutils::addFanToString; using irutils::addTempToString; +using irutils::addToggleToString; using irutils::addSwingVToString; using irutils::addIntToString; @@ -255,6 +256,11 @@ void IRLgAc::send(const uint16_t repeat) { _irsend.send(_protocol, getRaw(), kLgBits, repeat); // Some models have extra/special settings & controls switch (getModel()) { + case lg_ac_remote_model_t::LG6711A20083V: + // Only send the swing setting if we need to. + if (_swingv != _swingv_prev) + _irsend.send(_protocol, _swingv, kLgBits, repeat); + break; case lg_ac_remote_model_t::AKB74955603: // Only send the swing setting if we need to. if (_swingv != _swingv_prev) @@ -309,6 +315,7 @@ void IRLgAc::setModel(const lg_ac_remote_model_t model) { _protocol = decode_type_t::LG2; break; case lg_ac_remote_model_t::GE6711AR2853M: + case lg_ac_remote_model_t::LG6711A20083V: _protocol = decode_type_t::LG; break; default: @@ -319,24 +326,25 @@ void IRLgAc::setModel(const lg_ac_remote_model_t model) { /// Get the model of the A/C. /// @return The enum of the compatible model. -lg_ac_remote_model_t IRLgAc::getModel(void) const { - return _model; -} +lg_ac_remote_model_t IRLgAc::getModel(void) const { return _model; } /// Check if the stored code must belong to a AKB74955603 model. /// @return true, if it is AKB74955603 message. Otherwise, false. /// @note Internal use only. bool IRLgAc::_isAKB74955603(void) const { return ((_.raw & kLgAcAKB74955603DetectionMask) && _isNormal()) || - isSwingV() || isLightToggle(); + (isSwingV() && !isSwingVToggle()) || isLightToggle(); } /// Check if the stored code must belong to a AKB73757604 model. /// @return true, if it is AKB73757604 message. Otherwise, false. /// @note Internal use only. -bool IRLgAc::_isAKB73757604(void) const { - return isSwingH() || isVaneSwingV(); -} +bool IRLgAc::_isAKB73757604(void) const { return isSwingH() || isVaneSwingV(); } + +/// Check if the stored code must belong to a LG6711A20083V model. +/// @return true, if it is LG6711A20083V message. Otherwise, false. +/// @note Internal use only. +bool IRLgAc::_isLG6711A20083V(void) const { return isSwingVToggle(); } /// Get a copy of the internal state/code for this protocol. /// @return The code for this protocol based on the current internal state. @@ -353,7 +361,10 @@ void IRLgAc::setRaw(const uint32_t new_code, const decode_type_t protocol) { // Set the default model for this protocol, if the protocol is supplied. switch (protocol) { case decode_type_t::LG: - setModel(lg_ac_remote_model_t::GE6711AR2853M); + if (isSwingVToggle()) // This model uses a swingv toggle message. + setModel(lg_ac_remote_model_t::LG6711A20083V); + else // Assume others are a different model. + setModel(lg_ac_remote_model_t::GE6711AR2853M); break; case decode_type_t::LG2: setModel(lg_ac_remote_model_t::AKB75215403); @@ -521,18 +532,22 @@ void IRLgAc::setMode(const uint8_t mode) { } } +/// Check if the stored code is a SwingV Toggle message. +/// @return true, if it is. Otherwise, false. +bool IRLgAc::isSwingVToggle(void) const { return _.raw == kLgAcSwingVToggle; } + /// Check if the stored code is a Swing message. /// @return true, if it is. Otherwise, false. bool IRLgAc::isSwing(void) const { - return (_.raw >> 12) == kLgAcSwingSignature; + return ((_.raw >> 12) == kLgAcSwingSignature) || isSwingVToggle(); } /// Check if the stored code is a non-vane SwingV message. /// @return true, if it is. Otherwise, false. bool IRLgAc::isSwingV(void) const { const uint32_t code = _.raw >> kLgAcChecksumSize; - return code >= (kLgAcSwingVLowest >> kLgAcChecksumSize) && - code < (kLgAcSwingHAuto >> kLgAcChecksumSize); + return (code >= (kLgAcSwingVLowest >> kLgAcChecksumSize) && + code < (kLgAcSwingHAuto >> kLgAcChecksumSize)) || isSwingVToggle(); } /// Check if the stored code is a SwingH message. @@ -562,7 +577,7 @@ bool IRLgAc::isVaneSwingV(void) const { /// @param[in] position The position/mode to set the vanes to. void IRLgAc::setSwingV(const uint32_t position) { // Is it a valid position code? - if (position == kLgAcSwingVOff || + if (position == kLgAcSwingVOff || position == kLgAcSwingVToggle || toCommonSwingV(position) != stdAc::swingv_t::kOff) { if (position <= 0xFF) { // It's a short code, convert it. _swingv = (kLgAcSwingSignature << 8 | position) << kLgAcChecksumSize; @@ -703,6 +718,7 @@ stdAc::swingv_t IRLgAc::toCommonSwingV(const uint32_t code) { case kLgAcSwingVLow: return stdAc::swingv_t::kLow; case kLgAcSwingVLowest_Short: case kLgAcSwingVLowest: return stdAc::swingv_t::kLowest; + case kLgAcSwingVToggle: case kLgAcSwingVSwing_Short: case kLgAcSwingVSwing: return stdAc::swingv_t::kAuto; default: return stdAc::swingv_t::kOff; @@ -740,7 +756,7 @@ uint8_t IRLgAc::convertVaneSwingV(const stdAc::swingv_t swingv) { /// @param[in] prev Ptr to the previous state if required. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRLgAc::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; + stdAc::state_t result{}; // Start with the previous state if given it. if (prev != NULL) { result = *prev; @@ -752,13 +768,18 @@ stdAc::state_t IRLgAc::toCommon(const stdAc::state_t *prev) const { result.swingv = toCommonSwingV(getSwingV()); } result.protocol = _protocol; + if (isLightToggle()) { + result.light = !result.light; + return result; + } else { + result.light = _light; + } result.model = getModel(); result.power = getPower(); result.mode = toCommonMode(_.Mode); result.celsius = true; result.degrees = getTemp(); result.fanspeed = toCommonFanSpeed(_.Fan); - result.light = isLightToggle() ? !result.light : _light; if (isSwingV()) result.swingv = toCommonSwingV(getSwingV()); if (isVaneSwingV()) result.swingv = toCommonVaneSwingV(VANESWINGVPOS(getVaneCode(_.raw))); @@ -800,18 +821,21 @@ String IRLgAc::toString(void) const { } else if (isSwingH()) { result += addBoolToString(_swingh, kSwingHStr); } else if (isSwingV()) { - result += addSwingVToString((uint8_t)(_swingv >> kLgAcChecksumSize), - 0, // No Auto, See "swing". Unused - kLgAcSwingVHighest_Short, - kLgAcSwingVHigh_Short, - kLgAcSwingVUpperMiddle_Short, - kLgAcSwingVMiddle_Short, - 0, // Unused - kLgAcSwingVLow_Short, - kLgAcSwingVLowest_Short, - kLgAcSwingVOff_Short, - kLgAcSwingVSwing_Short, - 0, 0); + if (isSwingVToggle()) + result += addToggleToString(isSwingVToggle(), kSwingVStr); + else + result += addSwingVToString((uint8_t)(_swingv >> kLgAcChecksumSize), + 0, // No Auto, See "swing". Unused + kLgAcSwingVHighest_Short, + kLgAcSwingVHigh_Short, + kLgAcSwingVUpperMiddle_Short, + kLgAcSwingVMiddle_Short, + 0, // Unused + kLgAcSwingVLow_Short, + kLgAcSwingVLowest_Short, + kLgAcSwingVOff_Short, + kLgAcSwingVSwing_Short, + 0, 0); } else if (isVaneSwingV()) { const uint8_t vane = getVaneCode(_.raw) / kLgAcVaneSwingVSize; result += addIntToString(vane, kVaneStr); diff --git a/lib/IRremoteESP8266/src/ir_LG.h b/lib/IRremoteESP8266/src/ir_LG.h index 2bd7dbc1c..4f88ed2a8 100644 --- a/lib/IRremoteESP8266/src/ir_LG.h +++ b/lib/IRremoteESP8266/src/ir_LG.h @@ -6,7 +6,8 @@ /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1513 // Supports: -// Brand: LG, Model: 6711A20083V remote (LG) +// Brand: LG, Model: 6711A20083V remote (LG - LG6711A20083V) +// Brand: LG, Model: TS-H122ERM1 remote (LG - LG6711A20083V) // Brand: LG, Model: AKB74395308 remote (LG2) // Brand: LG, Model: S4-W12JA3AA A/C (LG2) // Brand: LG, Model: AKB75215403 remote (LG2) @@ -17,8 +18,8 @@ // Brand: LG, Model: AKB73757604 remote (LG2 - AKB73757604) // Brand: LG, Model: AKB73315611 remote (LG2 - AKB74955603) // Brand: LG, Model: MS05SQ NW0 A/C (LG2 - AKB74955603) -// Brand: General Electric, Model: AG1BH09AW101 Split A/C (LG) -// Brand: General Electric, Model: 6711AR2853M A/C Remote (LG) +// Brand: General Electric, Model: AG1BH09AW101 A/C (LG - GE6711AR2853M) +// Brand: General Electric, Model: 6711AR2853M Remote (LG - GE6711AR2853M) #ifndef IR_LG_H_ #define IR_LG_H_ @@ -73,6 +74,7 @@ const uint8_t kLgAcSignature = 0x88; const uint32_t kLgAcOffCommand = 0x88C0051; const uint32_t kLgAcLightToggle = 0x88C00A6; +const uint32_t kLgAcSwingVToggle = 0x8810001; const uint32_t kLgAcSwingSignature = 0x8813; const uint32_t kLgAcSwingVLowest = 0x8813048; const uint32_t kLgAcSwingVLow = 0x8813059; @@ -144,6 +146,7 @@ class IRLgAc { void setSwingH(const bool on); bool getSwingH(void) const; bool isSwingV(void) const; + bool isSwingVToggle(void) const; bool isVaneSwingV(void) const; void setSwingV(const uint32_t position); uint32_t getSwingV(void) const; @@ -192,6 +195,7 @@ class IRLgAc { void _setTemp(const uint8_t value); bool _isAKB74955603(void) const; bool _isAKB73757604(void) const; + bool _isLG6711A20083V(void) const; bool _isNormal(void) const; }; diff --git a/lib/IRremoteESP8266/src/ir_Midea.cpp b/lib/IRremoteESP8266/src/ir_Midea.cpp index 8b3b645d8..4fbc07973 100644 --- a/lib/IRremoteESP8266/src/ir_Midea.cpp +++ b/lib/IRremoteESP8266/src/ir_Midea.cpp @@ -42,6 +42,7 @@ using irutils::addIntToString; using irutils::addLabeledString; using irutils::addModeToString; using irutils::addTempToString; +using irutils::addToggleToString; using irutils::minsToString; #if SEND_MIDEA @@ -100,10 +101,13 @@ IRMideaAC::IRMideaAC(const uint16_t pin, const bool inverted, void IRMideaAC::stateReset(void) { // Power On, Mode Auto, Fan Auto, Temp = 25C/77F _.remote_state = 0xA1826FFFFF62; - _SwingVToggle = false; + _CleanToggle = false; _EconoToggle = false; - _TurboToggle = false; + _8CHeatToggle = false; _LightToggle = false; + _Quiet = _Quiet_prev = false; + _SwingVToggle = false; + _TurboToggle = false; #if KAYSUN_AC _SwingVStep = false; #endif // KAYSUN_AC @@ -135,6 +139,19 @@ void IRMideaAC::send(const uint16_t repeat) { if (_LightToggle && !isLightToggle()) _irsend.sendMidea(kMideaACToggleLight, kMideaBits, repeat); _LightToggle = false; + if (getMode() <= kMideaACAuto) { // Only available in Cool, Dry, or Auto mode + if (_CleanToggle && !isCleanToggle()) + _irsend.sendMidea(kMideaACToggleSelfClean, kMideaBits, repeat); + _CleanToggle = false; + } else if (getMode() == kMideaACHeat) { // Only available in Heat mode + if (_8CHeatToggle && !is8CHeatToggle()) + _irsend.sendMidea(kMideaACToggle8CHeat, kMideaBits, repeat); + _8CHeatToggle = false; + } + if (_Quiet != _Quiet_prev) + _irsend.sendMidea(_Quiet ? kMideaACQuietOn : kMideaACQuietOff, + kMideaBits, repeat); + _Quiet_prev = _Quiet; } #endif // SEND_MIDEA @@ -410,6 +427,75 @@ bool IRMideaAC::getLightToggle(void) { return _LightToggle; } +/// Is the current state a Self-Clean toggle message? +/// @return true, it is. false, it isn't. +bool IRMideaAC::isCleanToggle(void) const { + return _.remote_state == kMideaACToggleSelfClean; +} + +/// Set the A/C to toggle the Self Clean mode for the next send. +/// @note Only works in Cool, Dry, or Auto modes. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMideaAC::setCleanToggle(const bool on) { + _CleanToggle = on && getMode() <= kMideaACAuto; +} + +// Get the Self-Clean toggle state of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMideaAC::getCleanToggle(void) { + _CleanToggle |= isCleanToggle(); + return _CleanToggle; +} + +/// Is the current state a 8C Heat (Freeze Protect) toggle message? +/// @note Only works in Heat mode. +/// @return true, it is. false, it isn't. +bool IRMideaAC::is8CHeatToggle(void) const { + return _.remote_state == kMideaACToggle8CHeat; +} + +/// Set the A/C to toggle the 8C Heat (Freeze Protect) mode for the next send. +/// @note Only works in Heat mode. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMideaAC::set8CHeatToggle(const bool on) { + _8CHeatToggle = on && getMode() == kMideaACHeat; +} + +// Get the 8C Heat (Freeze Protect) toggle state of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMideaAC::get8CHeatToggle(void) { + _8CHeatToggle |= is8CHeatToggle(); + return _8CHeatToggle; +} + +/// Is the current state a Quiet(Silent) message? +/// @return true, it is. false, it isn't. +bool IRMideaAC::isQuiet(void) const { + return (_.remote_state == kMideaACQuietOff || + _.remote_state == kMideaACQuietOn); +} + +/// Set the Quiet (Silent) mode for the next send. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRMideaAC::setQuiet(const bool on) { _Quiet = on; } + +/// Set the Quiet (Silent) mode for the next send. +/// @param[in] on true, the setting is on. false, the setting is off. +/// @param[in] prev true, previously the setting was on. false, setting was off. +void IRMideaAC::setQuiet(const bool on, const bool prev) { + setQuiet(on); + _Quiet_prev = prev; +} + +// Get the Quiet (Silent) mode state of the A/C. +/// @return true, the setting is on. false, the setting is off. +bool IRMideaAC::getQuiet(void) const { + if (isQuiet()) + return _.remote_state == kMideaACQuietOn; + else + return _Quiet; +} + /// Calculate the checksum for a given state. /// @param[in] state The value to calc the checksum of. /// @return The calculated checksum value. @@ -566,7 +652,7 @@ stdAc::fanspeed_t IRMideaAC::toCommonFanSpeed(const uint8_t speed) { /// @param[in] prev A Ptr to the previous state. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) { - stdAc::state_t result; + stdAc::state_t result{}; if (prev != NULL) { result = *prev; } else { @@ -577,7 +663,6 @@ stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) { result.swingv = stdAc::swingv_t::kOff; result.quiet = false; result.turbo = false; - result.clean = false; result.econo = false; result.filter = false; result.light = false; @@ -597,6 +682,7 @@ stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) { result.fanspeed = toCommonFanSpeed(_.Fan); result.sleep = _.Sleep ? 0 : -1; result.econo = getEconoToggle(); + result.clean ^= getCleanToggle(); return result; } @@ -612,7 +698,7 @@ String IRMideaAC::toString(void) { case kMideaACTypeCommand: result += kCommandStr; break; case kMideaACTypeSpecial: result += kSpecialStr; break; case kMideaACTypeFollow: result += kFollowStr; break; - default: result += kUnknownStr; + default: result += kUnknownStr; } result += ')'; if (message_type != kMideaACTypeSpecial) { @@ -643,13 +729,16 @@ String IRMideaAC::toString(void) { kMideaACFanAuto, kMideaACFanAuto, kMideaACFanMed); result += addBoolToString(_.Sleep, kSleepStr); } - result += addBoolToString(getSwingVToggle(), kSwingVToggleStr); + result += addToggleToString(getSwingVToggle(), kSwingVStr); #if KAYSUN_AC result += addBoolToString(getSwingVStep(), kStepStr); #endif // KAYSUN_AC - result += addBoolToString(getEconoToggle(), kEconoToggleStr); - result += addBoolToString(getTurboToggle(), kTurboToggleStr); - result += addBoolToString(getLightToggle(), kLightToggleStr); + result += addToggleToString(getEconoToggle(), kEconoStr); + result += addToggleToString(getTurboToggle(), kTurboStr); + result += addBoolToString(getQuiet(), kQuietStr); + result += addToggleToString(getLightToggle(), kLightStr); + result += addToggleToString(getCleanToggle(), kCleanStr); + result += addToggleToString(get8CHeatToggle(), k8CHeatStr); return result; } diff --git a/lib/IRremoteESP8266/src/ir_Midea.h b/lib/IRremoteESP8266/src/ir_Midea.h index eaeaa04d8..e44055e79 100644 --- a/lib/IRremoteESP8266/src/ir_Midea.h +++ b/lib/IRremoteESP8266/src/ir_Midea.h @@ -5,6 +5,7 @@ /// Midea added by crankyoldgit & bwze /// @see https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1342#issuecomment-733721085 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1733 // Supports: // Brand: Pioneer System, Model: RYBO12GMFILCAD A/C (12K BTU) (MIDEA) @@ -22,8 +23,22 @@ // Brand: Danby, Model: DAC100BGUWDB (MIDEA) // Brand: Danby, Model: DAC120BGUWDB (MIDEA) // Brand: Danby, Model: R09C/BCGE remote (MIDEA) +// Brand: Trotec, Model: TROTEC PAC 2100 X (MIDEA) // Brand: Trotec, Model: TROTEC PAC 3900 X (MIDEA) // Brand: Trotec, Model: RG57H(B)/BGE remote (MIDEA) +// Brand: Trotec, Model: RG57H3(B)/BGCEF-M remote (MIDEA) +// Brand: Lennox, Model: RG57A6/BGEFU1 remote (MIDEA) +// Brand: Lennox, Model: MWMA009S4-3P A/C (MIDEA) +// Brand: Lennox, Model: MWMA012S4-3P A/C (MIDEA) +// Brand: Lennox, Model: MCFA indoor split A/C (MIDEA) +// Brand: Lennox, Model: MCFB indoor split A/C (MIDEA) +// Brand: Lennox, Model: MMDA indoor split A/C (MIDEA) +// Brand: Lennox, Model: MMDB indoor split A/C (MIDEA) +// Brand: Lennox, Model: MWMA indoor split A/C (MIDEA) +// Brand: Lennox, Model: MWMB indoor split A/C (MIDEA) +// Brand: Lennox, Model: M22A indoor split A/C (MIDEA) +// Brand: Lennox, Model: M33A indoor split A/C (MIDEA) +// Brand: Lennox, Model: M33B indoor split A/C (MIDEA) #ifndef IR_MIDEA_H_ #define IR_MIDEA_H_ @@ -123,9 +138,16 @@ const uint8_t kMideaACFanHigh = 3; // 0b11 // const uint64_t kMideaACToggleIonizer = 0xA201FFFFFF7C; kSwingVToggleStr = kIonStr; #endif // DANBY_DAC -const uint64_t kMideaACToggleEcono = 0xA202FFFFFF7E; -const uint64_t kMideaACToggleLight = 0xA208FFFFFF75; -const uint64_t kMideaACToggleTurbo = 0xA209FFFFFF74; +const uint64_t kMideaACToggleEcono = 0xA202FFFFFF7E; +const uint64_t kMideaACToggleLight = 0xA208FFFFFF75; +const uint64_t kMideaACToggleTurbo = 0xA209FFFFFF74; +// Mode must be Auto, Cool, or Dry +const uint64_t kMideaACToggleSelfClean = 0xA20DFFFFFF70; +// 8C Heat AKA Freeze Protection +const uint64_t kMideaACToggle8CHeat = 0xA20FFFFFFF73; // Only in Heat +const uint64_t kMideaACQuietOn = 0xA212FFFFFF6E; +const uint64_t kMideaACQuietOff = 0xA213FFFFFF6F; + const uint8_t kMideaACTypeCommand = 0b001; ///< Message type const uint8_t kMideaACTypeSpecial = 0b010; ///< Message type const uint8_t kMideaACTypeFollow = 0b100; ///< Message type @@ -202,6 +224,16 @@ class IRMideaAC { bool isLightToggle(void) const; void setLightToggle(const bool on); bool getLightToggle(void); + bool isCleanToggle(void) const; + void setCleanToggle(const bool on); + bool getCleanToggle(void); + bool is8CHeatToggle(void) const; + void set8CHeatToggle(const bool on); + bool get8CHeatToggle(void); + bool isQuiet(void) const; + void setQuiet(const bool on); + void setQuiet(const bool on, const bool prev); + bool getQuiet(void) const; uint8_t getType(void) const; bool isOnTimerEnabled(void) const; uint16_t getOnTimer(void) const; @@ -225,13 +257,17 @@ class IRMideaAC { /// @endcond #endif // UNIT_TEST MideaProtocol _; + bool _CleanToggle; + bool _EconoToggle; + bool _8CHeatToggle; + bool _LightToggle; + bool _Quiet; + bool _Quiet_prev; bool _SwingVToggle; #if KAYSUN_AC bool _SwingVStep; #endif // KAYSUN_AC - bool _EconoToggle; bool _TurboToggle; - bool _LightToggle; void checksum(void); static uint8_t calcChecksum(const uint64_t state); void setType(const uint8_t type); diff --git a/lib/IRremoteESP8266/src/ir_Mirage.cpp b/lib/IRremoteESP8266/src/ir_Mirage.cpp index a806937b1..e3e7c60d3 100644 --- a/lib/IRremoteESP8266/src/ir_Mirage.cpp +++ b/lib/IRremoteESP8266/src/ir_Mirage.cpp @@ -733,7 +733,7 @@ stdAc::swingv_t IRMirageAc::toCommonSwingV(const uint8_t pos) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRMirageAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::MIRAGE; result.model = _model; result.power = getPower(); diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp index 1e826a72d..286072bdc 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.cpp @@ -62,7 +62,7 @@ const uint16_t kMitsubishiAcBitMark = 450; const uint16_t kMitsubishiAcOneSpace = 1300; const uint16_t kMitsubishiAcZeroSpace = 420; const uint16_t kMitsubishiAcRptMark = 440; -const uint16_t kMitsubishiAcRptSpace = 17100; +const uint16_t kMitsubishiAcRptSpace = 15500; const uint8_t kMitsubishiAcExtraTolerance = 5; // Mitsubishi 136 bit A/C @@ -781,7 +781,7 @@ stdAc::swingh_t IRMitsubishiAC::toCommonSwingH(const uint8_t pos) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRMitsubishiAC::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::MITSUBISHI_AC; result.model = -1; // No models used. result.power = _.Power; @@ -1195,7 +1195,7 @@ stdAc::swingv_t IRMitsubishi136::toCommonSwingV(const uint8_t pos) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRMitsubishi136::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::MITSUBISHI136; result.model = -1; // No models used. result.power = _.Power; @@ -1660,7 +1660,7 @@ stdAc::swingh_t IRMitsubishi112::toCommonSwingH(const uint8_t pos) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRMitsubishi112::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::MITSUBISHI112; result.model = -1; // No models used. result.power = _.Power; diff --git a/lib/IRremoteESP8266/src/ir_Mitsubishi.h b/lib/IRremoteESP8266/src/ir_Mitsubishi.h index 55afcdce6..85e61eefd 100644 --- a/lib/IRremoteESP8266/src/ir_Mitsubishi.h +++ b/lib/IRremoteESP8266/src/ir_Mitsubishi.h @@ -35,6 +35,7 @@ // Brand: Mitsubishi Electric, Model: SG15D remote (MITSUBISHI_AC) // Brand: Mitsubishi Electric, Model: MSZ-ZW4017S A/C (MITSUBISHI_AC) // Brand: Mitsubishi Electric, Model: MSZ-FHnnVE A/C (MITSUBISHI_AC) +// Brand: Mitsubishi Electric, Model: RH151 remote (MITSUBISHI_AC) #ifndef IR_MITSUBISHI_H_ #define IR_MITSUBISHI_H_ diff --git a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp index b4a6ffc35..8cefc0683 100644 --- a/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp +++ b/lib/IRremoteESP8266/src/ir_MitsubishiHeavy.cpp @@ -454,7 +454,7 @@ stdAc::swingv_t IRMitsubishiHeavy152Ac::toCommonSwingV(const uint8_t pos) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRMitsubishiHeavy152Ac::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::MITSUBISHI_HEAVY_152; result.model = -1; // No models used. result.power = _.Power; @@ -899,7 +899,7 @@ stdAc::swingv_t IRMitsubishiHeavy88Ac::toCommonSwingV(const uint8_t pos) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRMitsubishiHeavy88Ac::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::MITSUBISHI_HEAVY_88; result.model = -1; // No models used. result.power = _.Power; diff --git a/lib/IRremoteESP8266/src/ir_Neoclima.cpp b/lib/IRremoteESP8266/src/ir_Neoclima.cpp index ff26e6c64..ac57714e9 100644 --- a/lib/IRremoteESP8266/src/ir_Neoclima.cpp +++ b/lib/IRremoteESP8266/src/ir_Neoclima.cpp @@ -484,7 +484,7 @@ bool IRNeoclimaAc::getFollow(void) const { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRNeoclimaAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::NEOCLIMA; result.model = -1; // No models used. result.power = _.Power; diff --git a/lib/IRremoteESP8266/src/ir_Panasonic.cpp b/lib/IRremoteESP8266/src/ir_Panasonic.cpp index e2add03f1..a0a0b6b23 100644 --- a/lib/IRremoteESP8266/src/ir_Panasonic.cpp +++ b/lib/IRremoteESP8266/src/ir_Panasonic.cpp @@ -760,7 +760,7 @@ stdAc::swingv_t IRPanasonicAc::toCommonSwingV(const uint8_t pos) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRPanasonicAc::toCommon(void) { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::PANASONIC_AC; result.model = getModel(); result.power = getPower(); @@ -1307,7 +1307,7 @@ String IRPanasonicAc32::toString(void) const { /// @param[in] prev Ptr to the previous state if required. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRPanasonicAc32::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; + stdAc::state_t result{}; // Start with the previous state if given it. if (prev != NULL) { result = *prev; diff --git a/lib/IRremoteESP8266/src/ir_Pioneer.cpp b/lib/IRremoteESP8266/src/ir_Pioneer.cpp index 90f58c6ed..6efe15c0a 100644 --- a/lib/IRremoteESP8266/src/ir_Pioneer.cpp +++ b/lib/IRremoteESP8266/src/ir_Pioneer.cpp @@ -9,6 +9,7 @@ /// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/547 /// @see https://www.pioneerelectronics.com/PUSA/Support/Home-Entertainment-Custom-Install/IR+Codes/A+V+Receivers /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1220 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1749#issuecomment-1028122645 // Supports: // Brand: Pioneer, Model: AV Receivers @@ -74,6 +75,10 @@ void IRsend::sendPioneer(const uint64_t data, const uint16_t nbits, /// and the second the command. /// e.g. /// "A556+AF20" is an Address of 0xA556 & a Command of 0xAF20. +/// @note If the Address is 0, use it like the following: +/// `irsend.sendPioneer(irsend.encodePioneer(0, 0xAA1C), 32, 1);` or +/// `irsend.sendPioneer(irsend.encodePioneer(0xAA1C, 0xAA1C), 64, 0);` +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1749#issuecomment-1028122645 uint64_t IRsend::encodePioneer(const uint16_t address, const uint16_t command) { return (((uint64_t)encodeNEC(address >> 8, address & 0xFF)) << 32) | encodeNEC(command >> 8, command & 0xFF); diff --git a/lib/IRremoteESP8266/src/ir_Rhoss.cpp b/lib/IRremoteESP8266/src/ir_Rhoss.cpp index f906f49be..c4b4652b2 100644 --- a/lib/IRremoteESP8266/src/ir_Rhoss.cpp +++ b/lib/IRremoteESP8266/src/ir_Rhoss.cpp @@ -324,7 +324,7 @@ stdAc::fanspeed_t IRRhossAc::toCommonFanSpeed(const uint8_t speed) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRRhossAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::RHOSS; result.power = getPower(); result.mode = toCommonMode(_.Mode); diff --git a/lib/IRremoteESP8266/src/ir_Samsung.cpp b/lib/IRremoteESP8266/src/ir_Samsung.cpp index 94cb52bba..b3fe2a860 100644 --- a/lib/IRremoteESP8266/src/ir_Samsung.cpp +++ b/lib/IRremoteESP8266/src/ir_Samsung.cpp @@ -866,7 +866,7 @@ stdAc::fanspeed_t IRSamsungAc::toCommonFanSpeed(const uint8_t spd) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRSamsungAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::SAMSUNG_AC; result.model = -1; // Not supported. result.power = getPower(); diff --git a/lib/IRremoteESP8266/src/ir_Sanyo.cpp b/lib/IRremoteESP8266/src/ir_Sanyo.cpp index 7dbed5a58..4a38e8e09 100644 --- a/lib/IRremoteESP8266/src/ir_Sanyo.cpp +++ b/lib/IRremoteESP8266/src/ir_Sanyo.cpp @@ -77,6 +77,14 @@ const uint32_t kSanyoAc88Gap = 3675; ///< uSeconds const uint16_t kSanyoAc88Freq = 38000; ///< Hz. (Guess only) const uint8_t kSanyoAc88ExtraTolerance = 5; /// (%) Extra tolerance to use. +const uint16_t kSanyoAc152HdrMark = 3300; ///< uSeconds +const uint16_t kSanyoAc152BitMark = 440; ///< uSeconds +const uint16_t kSanyoAc152HdrSpace = 1725; ///< uSeconds +const uint16_t kSanyoAc152OneSpace = 1290; ///< uSeconds +const uint16_t kSanyoAc152ZeroSpace = 405; ///< uSeconds +const uint16_t kSanyoAc152Freq = 38000; ///< Hz. (Guess only) +const uint8_t kSanyoAc152ExtraTolerance = 13; /// (%) Extra tolerance to use. + #if SEND_SANYO /// Construct a Sanyo LC7461 message. /// @param[in] address The 13 bit value of the address(Custom) portion of the @@ -607,7 +615,7 @@ void IRSanyoAc::setOffTimer(const uint16_t mins) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRSanyoAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::SANYO_AC; result.model = -1; // Not supported. result.power = getPower(); @@ -687,7 +695,7 @@ void IRsend::sendSanyoAc88(const uint8_t data[], const uint16_t nbytes, #endif // SEND_SANYO_AC88 #if DECODE_SANYO_AC88 -/// Decode the supplied SanyoAc message. +/// Decode the supplied SanyoAc88 message. /// Status: ALPHA / Untested. /// @param[in,out] results Ptr to the data to decode & where to store the decode /// @warning data's bit order may change. It is not yet confirmed. @@ -935,7 +943,7 @@ bool IRSanyoAc88::getSleep(void) const { return _.Sleep; } /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRSanyoAc88::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::SANYO_AC88; result.model = -1; // Not supported. result.power = getPower(); @@ -976,3 +984,61 @@ String IRSanyoAc88::toString(void) const { result += addLabeledString(minsToString(getClock()), kClockStr); return result; } + +#if SEND_SANYO_AC152 +/// Send a SanyoAc152 formatted message. +/// Status: BETA / Probably works. +/// @param[in] data An array of bytes containing the IR command. +/// @warning data's bit order may change. It is not yet confirmed. +/// @param[in] nbytes Nr. of bytes of data in the array. +/// @param[in] repeat Nr. of times the message is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1826 +void IRsend::sendSanyoAc152(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + // (Header + Data + Footer) per repeat + sendGeneric(kSanyoAc152HdrMark, kSanyoAc152HdrSpace, + kSanyoAc152BitMark, kSanyoAc152OneSpace, + kSanyoAc152BitMark, kSanyoAc152ZeroSpace, + kSanyoAc152BitMark, kDefaultMessageGap, + data, nbytes, kSanyoAc152Freq, false, repeat, kDutyDefault); + space(kDefaultMessageGap); // Make a guess at a post message gap. +} +#endif // SEND_SANYO_AC152 + +#if DECODE_SANYO_AC152 +/// Decode the supplied SanyoAc152 message. +/// Status: BETA / Probably works. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// @warning data's bit order may change. It is not yet confirmed. +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1503 +bool IRrecv::decodeSanyoAc152(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kSanyoAc152Bits) + return false; + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kSanyoAc152HdrMark, kSanyoAc152HdrSpace, + kSanyoAc152BitMark, kSanyoAc152OneSpace, + kSanyoAc152BitMark, kSanyoAc152ZeroSpace, + kSanyoAc152BitMark, + kDefaultMessageGap, // Just a guess. + false, _tolerance + kSanyoAc152ExtraTolerance, + kMarkExcess, false)) + return false; // No match! + + // Success + results->decode_type = decode_type_t::SANYO_AC152; + 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_SANYO_AC152 diff --git a/lib/IRremoteESP8266/src/ir_Sanyo.h b/lib/IRremoteESP8266/src/ir_Sanyo.h index 66376328f..42575cb3b 100644 --- a/lib/IRremoteESP8266/src/ir_Sanyo.h +++ b/lib/IRremoteESP8266/src/ir_Sanyo.h @@ -13,6 +13,7 @@ /// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?usp=sharing /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1503 /// @see https://docs.google.com/spreadsheets/d/1weUmGAsEpfX38gg5rlDN69Uchnbr6gQl9FqHffLBIRk/edit#gid=0 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1826 // Supports: // Brand: Sanyo, Model: SA 8650B - disabled @@ -21,6 +22,8 @@ // Brand: Sanyo, Model: RCS-2HS4E remote (SANYO_AC) // Brand: Sanyo, Model: SAP-K242AH A/C (SANYO_AC) // Brand: Sanyo, Model: RCS-2S4E remote (SANYO_AC) +// Brand: Sanyo, Model: RCS-4MHVPIS4EE remote (SANYO_AC152) +// Brand: Sanyo, Model: SAP-KMRV124EHE A/C (SANYO_AC152) #ifndef IR_SANYO_H_ #define IR_SANYO_H_ diff --git a/lib/IRremoteESP8266/src/ir_Sharp.cpp b/lib/IRremoteESP8266/src/ir_Sharp.cpp index 38f0ac32c..d184c9301 100644 --- a/lib/IRremoteESP8266/src/ir_Sharp.cpp +++ b/lib/IRremoteESP8266/src/ir_Sharp.cpp @@ -837,7 +837,7 @@ stdAc::swingv_t IRSharpAc::toCommonSwingV(const uint8_t pos, /// @param[in] prev Ptr to the previous state if required. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRSharpAc::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; + stdAc::state_t result{}; // Start with the previous state if given it. if (prev != NULL) result = *prev; result.protocol = decode_type_t::SHARP_AC; diff --git a/lib/IRremoteESP8266/src/ir_Tcl.cpp b/lib/IRremoteESP8266/src/ir_Tcl.cpp index a9e8784a3..45e210f2b 100644 --- a/lib/IRremoteESP8266/src/ir_Tcl.cpp +++ b/lib/IRremoteESP8266/src/ir_Tcl.cpp @@ -1,4 +1,4 @@ -// Copyright 2019, 2021 David Conran +// Copyright 2019, 2021, 2022 David Conran /// @file /// @brief Support for TCL protocols. @@ -14,10 +14,19 @@ #include "IRutils.h" // Constants - const uint8_t kTcl112AcTimerResolution = 20; // Minutes const uint16_t kTcl112AcTimerMax = 720; // Minutes (12 hrs) +const uint16_t kTcl96AcHdrMark = 1056; // uSeconds. +const uint16_t kTcl96AcHdrSpace = 550; // uSeconds. +const uint16_t kTcl96AcBitMark = 600; // uSeconds. +const uint32_t kTcl96AcGap = kDefaultMessageGap; // Just a guess. +const uint8_t kTcl96AcSpaceCount = 4; +const uint16_t kTcl96AcBitSpaces[kTcl96AcSpaceCount] = {360, // 0b00 + 838, // 0b01 + 2182, // 0b10 + 1444}; // 0b11 + using irutils::addBoolToString; using irutils::addFanToString; using irutils::addIntToString; @@ -444,7 +453,7 @@ stdAc::swingv_t IRTcl112Ac::toCommonSwingV(const uint8_t setting) { /// @param[in] prev Ptr to the previous state if required. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRTcl112Ac::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; + stdAc::state_t result{}; // Start with the previous state if given it. if (prev != NULL) result = *prev; result.protocol = decode_type_t::TCL112AC; @@ -527,3 +536,81 @@ String IRTcl112Ac::toString(void) const { /// It's the same as `decodeMitsubishi112()`. A shared routine is used. /// You can find it in: ir_Mitsubishi.cpp #endif // DECODE_TCL112AC + +#if SEND_TCL96AC +/// Send a TCL 96-bit A/C message. +/// Status: BETA / Untested on a real device working. +/// @param[in] data The message to be sent. +/// @param[in] nbytes The number of bytes of message to be sent. +/// @param[in] repeat The number of times the command is to be repeated. +void IRsend::sendTcl96Ac(const unsigned char data[], const uint16_t nbytes, + const uint16_t repeat) { + enableIROut(38); + for (uint16_t r = 0; r <= repeat; r++) { + // Header + mark(kTcl96AcHdrMark); + space(kTcl96AcHdrSpace); + // Data + for (uint16_t pos = 0; pos < nbytes; pos++) { + uint8_t databyte = data[pos]; + for (uint8_t bits = 0; bits < 8; bits += 2) { + mark(kTcl96AcBitMark); + space(kTcl96AcBitSpaces[GETBITS8(databyte, 8 - 2, 2)]); + databyte <<= 2; + } + } + // Footer + mark(kTcl96AcBitMark); + space(kTcl96AcGap); + } +} +#endif // SEND_TCL96AC + +#if DECODE_TCL96AC +/// Decode the supplied Tcl96Ac message. +/// Status: ALPHA / Experimental. +/// @param[in,out] results Ptr to the data to decode & where to store the result +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return True if it can decode it, false if it can't. +bool IRrecv::decodeTcl96Ac(decode_results* results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < nbits + kHeader + kFooter - 1 + offset) + return false; // Message is smaller than we expected. + if (strict && nbits != kTcl96AcBits) + return false; // Not strictly a TCL96AC message. + uint8_t data = 0; + // Header. + if (!matchMark(results->rawbuf[offset++], kTcl96AcHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kTcl96AcHdrSpace)) return false; + // Data (2 bits at a time) + for (uint16_t bits_so_far = 0; bits_so_far < nbits; bits_so_far += 2) { + if (bits_so_far % 8) + data <<= 2; // Make space for the new data bits. + else + data = 0; + if (!matchMark(results->rawbuf[offset++], kTcl96AcBitMark)) return false; + uint8_t value = 0; + while (value < kTcl96AcSpaceCount) { + if (matchSpace(results->rawbuf[offset], kTcl96AcBitSpaces[value])) { + data += value; + break; + } + value++; + } + if (value >= kTcl96AcSpaceCount) return false; // No matches. + offset++; + *(results->state + bits_so_far / 8) = data; + } + // Footer + if (!matchMark(results->rawbuf[offset++], kTcl96AcBitMark)) return false; + if (offset < results->rawlen && + !matchAtLeast(results->rawbuf[offset], kTcl96AcGap)) return false; + // Success + results->decode_type = TCL96AC; + results->bits = nbits; + return true; +} +#endif // DECODE_TCL96AC diff --git a/lib/IRremoteESP8266/src/ir_Tcl.h b/lib/IRremoteESP8266/src/ir_Tcl.h index c7ae038d0..805270aa2 100644 --- a/lib/IRremoteESP8266/src/ir_Tcl.h +++ b/lib/IRremoteESP8266/src/ir_Tcl.h @@ -10,6 +10,7 @@ // Brand: Teknopoint, Model: GZ-055B-E1 remote (GZ055BE1) // Brand: Daewoo, Model: DSB-F0934ELH-V A/C // Brand: Daewoo, Model: GYKQ-52E remote +// Brand: TCL, Model: GYKQ-58(XM) remote (TCL96AC) #ifndef IR_TCL_H_ #define IR_TCL_H_ diff --git a/lib/IRremoteESP8266/src/ir_Technibel.cpp b/lib/IRremoteESP8266/src/ir_Technibel.cpp index d58cc7e05..bf9b799d5 100644 --- a/lib/IRremoteESP8266/src/ir_Technibel.cpp +++ b/lib/IRremoteESP8266/src/ir_Technibel.cpp @@ -363,7 +363,7 @@ uint16_t IRTechnibelAc::getTimer(void) const { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRTechnibelAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::TECHNIBEL_AC; result.power = _.Power; result.mode = toCommonMode(_.Mode); diff --git a/lib/IRremoteESP8266/src/ir_Teco.cpp b/lib/IRremoteESP8266/src/ir_Teco.cpp index 031691edd..289e15d52 100644 --- a/lib/IRremoteESP8266/src/ir_Teco.cpp +++ b/lib/IRremoteESP8266/src/ir_Teco.cpp @@ -294,7 +294,7 @@ stdAc::fanspeed_t IRTecoAc::toCommonFanSpeed(const uint8_t speed) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRTecoAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::TECO; result.model = -1; // Not supported. result.power = _.Power; diff --git a/lib/IRremoteESP8266/src/ir_Teknopoint.cpp b/lib/IRremoteESP8266/src/ir_Teknopoint.cpp index 0feee6d70..4a5eef9be 100644 --- a/lib/IRremoteESP8266/src/ir_Teknopoint.cpp +++ b/lib/IRremoteESP8266/src/ir_Teknopoint.cpp @@ -6,6 +6,7 @@ // Supports: // Brand: Teknopoint, Model: Allegro SSA-09H A/C // Brand: Teknopoint, Model: GZ-055B-E1 remote +// Brand: Teknopoint, Model: GZ01-BEJ0-000 remote #include "IRrecv.h" #include "IRsend.h" @@ -73,3 +74,8 @@ bool IRrecv::decodeTeknopoint(decode_results *results, uint16_t offset, return true; } #endif // DECODE_TEKNOPOINT + +// Looking for the IRTeknopoint/IRTeknopointAc class? +// It doesn't exist, it is instead part of the `IRTcl112Ac` class. +// i.e. use `IRTcl112Ac::setModel(tcl_ac_remote_model_t::GZ055BE1);` for +// Teknopoint A/Cs. diff --git a/lib/IRremoteESP8266/src/ir_Toshiba.cpp b/lib/IRremoteESP8266/src/ir_Toshiba.cpp index 7333493ef..c9672bcab 100644 --- a/lib/IRremoteESP8266/src/ir_Toshiba.cpp +++ b/lib/IRremoteESP8266/src/ir_Toshiba.cpp @@ -409,7 +409,7 @@ stdAc::fanspeed_t IRToshibaAC::toCommonFanSpeed(const uint8_t spd) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRToshibaAC::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; + stdAc::state_t result{}; // Start with the previous state if given it. if (prev != NULL) { result = *prev; diff --git a/lib/IRremoteESP8266/src/ir_Toto.cpp b/lib/IRremoteESP8266/src/ir_Toto.cpp new file mode 100644 index 000000000..2997e760e --- /dev/null +++ b/lib/IRremoteESP8266/src/ir_Toto.cpp @@ -0,0 +1,131 @@ +// Copyright 2022 David Conran (crankyoldgit) +/// @file +/// @brief Support for the Toto Toilet IR protocols. +/// @see https://www.d-resi.jp/dt/nishi-shinjuku/limited/imgs/pdf/book6.pdf +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1806 + +// Supports: +// Brand: Toto, Model: Washlet Toilet NJ + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Constants +const uint16_t kTotoHdrMark = 6197; +const uint16_t kTotoHdrSpace = 2754; +const uint16_t kTotoBitMark = 600; +const uint16_t kTotoOneSpace = 1634; +const uint16_t kTotoZeroSpace = 516; +const uint16_t kTotoGap = 38000; +const uint16_t kTotoSpecialGap = 42482; +const uint64_t kTotoPrefix = 0x0802; +const uint16_t kTotoPrefixBits = 15; + +#if SEND_TOTO +/// Send a Toto Toilet formatted message. +/// Status: BETA / Seems to work. +/// @param[in] data The message to be sent. +/// @param[in] nbits The number of bits of message to be sent. +/// @param[in] repeat The number of times the command is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1806 +void IRsend::sendToto(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + if (nbits <= kTotoShortBits) { // Short code message. + sendGeneric(kTotoHdrMark, kTotoHdrSpace, + kTotoBitMark, kTotoOneSpace, + kTotoBitMark, kTotoZeroSpace, + kTotoBitMark, kTotoGap, + (data << kTotoPrefixBits) | kTotoPrefix, + nbits + kTotoPrefixBits, + 38, false, repeat, kDutyDefault); + } else { // Long code message + // This is really two messages sent at least one extra time each. + sendToto(data >> kTotoShortBits, nbits - kTotoShortBits, repeat + 1); + sendToto(GETBITS64(data, 0, kTotoShortBits), kTotoShortBits, repeat + 1); + } +} +#endif // SEND_TOTO + +#if DECODE_TOTO +/// Decode the supplied Toto Toilet message. +/// Status: ALPHA / Untested. +/// @param[in,out] results Ptr to the data to decode & where to store the result +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return True if it can decode it, false if it can't. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1806 +bool IRrecv::decodeToto(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kTotoShortBits && nbits != kTotoLongBits) + return false; // We expect Toto to be a certain sized messages. + + const uint16_t repeats = (nbits > kTotoShortBits) ? kTotoDefaultRepeat + 1 + : kTotoDefaultRepeat; + const uint16_t ksections = (nbits > kTotoShortBits) ? 2 : 1; + const uint16_t ksection_bits = nbits / ksections; + + if (results->rawlen < (2 * (nbits + kTotoPrefixBits) + kHeader + kFooter) * + (repeats + 1) - 1 + offset) + return false; // We don't have enough entries to possibly match. + + uint16_t used = 0; + + // Long messages have two sections, short have only one. + for (uint16_t section = 1; section <= ksections; section++) { + results->value <<= ksection_bits; + uint64_t data = 0; + uint64_t repeat_data = 0; + for (uint16_t r = 0; r <= repeats; r++) { // We expect a repeat. + uint64_t prefix = 0; + // Header + Prefix + used = matchGeneric(results->rawbuf + offset, &prefix, + results->rawlen - offset, kTotoPrefixBits, + kTotoHdrMark, kTotoHdrSpace, + kTotoBitMark, kTotoOneSpace, + kTotoBitMark, kTotoZeroSpace, + 0, 0, false, // No Footer + kUseDefTol, kMarkExcess, false); + if (!used) return false; // Didn't match, so fail. + offset += used; + if (strict && (prefix != kTotoPrefix)) return false; + // Data + Footer + Gap + used = matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, ksection_bits, + 0, 0, // No Header + kTotoBitMark, kTotoOneSpace, + kTotoBitMark, kTotoZeroSpace, + kTotoBitMark, kTotoGap, true, + kUseDefTol, kMarkExcess, false); + if (!used) return false; // Didn't match, so fail. + offset += used; + if (strict) { + if (r && data != repeat_data) return false; // Repeat didn't match. + // Integrity check. + uint8_t result = 0; + uint64_t check = data; + uint16_t bits = 0; + // Loop over the data 8 bits at a time. + while (bits < ksection_bits) { + result ^= (check & 0b111111111); // Xor with the last 8 bits. + bits += 8; + check >>= 8; + } + if (result) return false; // Intergrity check failed. + } + repeat_data = data; + } + results->value |= data; + } + // Success + results->bits = nbits; + results->decode_type = decode_type_t::TOTO; + results->command = GETBITS64(results->value, 0, ksection_bits - 8); + results->address = GETBITS64(results->value, ksection_bits, + ksection_bits - 8); + return true; +} +#endif // DECODE_TOTO diff --git a/lib/IRremoteESP8266/src/ir_Transcold.cpp b/lib/IRremoteESP8266/src/ir_Transcold.cpp index 14cc7f8bd..0de351368 100644 --- a/lib/IRremoteESP8266/src/ir_Transcold.cpp +++ b/lib/IRremoteESP8266/src/ir_Transcold.cpp @@ -339,7 +339,7 @@ stdAc::fanspeed_t IRTranscoldAc::toCommonFanSpeed(const uint8_t speed) { /// @param[in] prev Ptr to the previous state if required. /// @return A stdAc::state_t state. stdAc::state_t IRTranscoldAc::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; + stdAc::state_t result{}; // Start with the previous state if given it. if (prev != NULL) { result = *prev; diff --git a/lib/IRremoteESP8266/src/ir_Trotec.cpp b/lib/IRremoteESP8266/src/ir_Trotec.cpp index 7cd6fff5e..c7de83aea 100644 --- a/lib/IRremoteESP8266/src/ir_Trotec.cpp +++ b/lib/IRremoteESP8266/src/ir_Trotec.cpp @@ -266,7 +266,7 @@ stdAc::fanspeed_t IRTrotecESP::toCommonFanSpeed(const uint8_t spd) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRTrotecESP::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::TROTEC; result.power = _.Power; result.mode = toCommonMode(_.Mode); @@ -601,7 +601,7 @@ stdAc::fanspeed_t IRTrotec3550::toCommonFanSpeed(const uint8_t spd) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRTrotec3550::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::TROTEC_3550; result.power = _.Power; result.mode = toCommonMode(_.Mode); diff --git a/lib/IRremoteESP8266/src/ir_Truma.cpp b/lib/IRremoteESP8266/src/ir_Truma.cpp index 853c640e1..55033efb8 100644 --- a/lib/IRremoteESP8266/src/ir_Truma.cpp +++ b/lib/IRremoteESP8266/src/ir_Truma.cpp @@ -295,7 +295,7 @@ stdAc::fanspeed_t IRTrumaAc::toCommonFanSpeed(const uint8_t spd) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRTrumaAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::TRUMA; result.model = -1; // Not supported. diff --git a/lib/IRremoteESP8266/src/ir_Vestel.cpp b/lib/IRremoteESP8266/src/ir_Vestel.cpp index 803363a16..c0b47078a 100644 --- a/lib/IRremoteESP8266/src/ir_Vestel.cpp +++ b/lib/IRremoteESP8266/src/ir_Vestel.cpp @@ -442,7 +442,7 @@ stdAc::fanspeed_t IRVestelAc::toCommonFanSpeed(const uint8_t spd) { /// Convert the current internal state into its stdAc::state_t equivalent. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRVestelAc::toCommon(void) const { - stdAc::state_t result; + stdAc::state_t result{}; result.protocol = decode_type_t::VESTEL_AC; result.model = -1; // Not supported. result.power = _.Power; diff --git a/lib/IRremoteESP8266/src/ir_Voltas.cpp b/lib/IRremoteESP8266/src/ir_Voltas.cpp index 847446f38..9837e0605 100644 --- a/lib/IRremoteESP8266/src/ir_Voltas.cpp +++ b/lib/IRremoteESP8266/src/ir_Voltas.cpp @@ -454,7 +454,7 @@ void IRVoltas::setOffTime(const uint16_t nr_of_mins) { /// @param[in] prev Ptr to the previous state if available. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRVoltas::toCommon(const stdAc::state_t *prev) { - stdAc::state_t result; + stdAc::state_t result{}; // Start with the previous state if given it. if (prev != NULL) { result = *prev; diff --git a/lib/IRremoteESP8266/src/ir_Voltas.h b/lib/IRremoteESP8266/src/ir_Voltas.h index cf79d3458..d8ad77bcb 100644 --- a/lib/IRremoteESP8266/src/ir_Voltas.h +++ b/lib/IRremoteESP8266/src/ir_Voltas.h @@ -9,6 +9,7 @@ // // Ref: https://docs.google.com/spreadsheets/d/1zzDEUQ52y7MZ7_xCU3pdjdqbRXOwZLsbTGvKWcicqCI/ // Ref: https://www.corona.co.jp/box/download.php?id=145060636229 +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/files/8646964/Voltas.Window.AC.122LZF.Remote.Instructions.pdf #ifndef IR_VOLTAS_H_ #define IR_VOLTAS_H_ diff --git a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp index 0bba57bfe..6dde68401 100644 --- a/lib/IRremoteESP8266/src/ir_Whirlpool.cpp +++ b/lib/IRremoteESP8266/src/ir_Whirlpool.cpp @@ -493,7 +493,7 @@ stdAc::fanspeed_t IRWhirlpoolAc::toCommonFanSpeed(const uint8_t speed) { /// @param[in] prev Ptr to the previous state if required. /// @return The stdAc equivalent of the native settings. stdAc::state_t IRWhirlpoolAc::toCommon(const stdAc::state_t *prev) const { - stdAc::state_t result; + stdAc::state_t result{}; // Start with the previous state if given it. if (prev != NULL) { result = *prev; diff --git a/lib/IRremoteESP8266/src/locale/defaults.h b/lib/IRremoteESP8266/src/locale/defaults.h index d8142e20e..40b85c27c 100644 --- a/lib/IRremoteESP8266/src/locale/defaults.h +++ b/lib/IRremoteESP8266/src/locale/defaults.h @@ -566,6 +566,9 @@ D_STR_INDIRECT " " D_STR_MODE #ifndef D_STR_YBOFB #define D_STR_YBOFB "YBOFB" #endif // D_STR_YBOFB +#ifndef D_STR_YX1FSF +#define D_STR_YX1FSF "YX1FSF" +#endif // D_STR_YX1FSF #ifndef D_STR_V9014557_A #define D_STR_V9014557_A "V9014557-A" #endif // D_STR_V9014557_A @@ -608,6 +611,9 @@ D_STR_INDIRECT " " D_STR_MODE #ifndef D_STR_AKB73757604 #define D_STR_AKB73757604 "AKB73757604" #endif // D_STR_AKB73757604 +#ifndef D_STR_LG6711A20083V +#define D_STR_LG6711A20083V "LG6711A20083V" +#endif // D_STR_LG6711A20083V #ifndef D_STR_KKG9AC1 #define D_STR_KKG9AC1 "KKG9AC1" #endif // D_STR_KKG9AC1 @@ -703,6 +709,12 @@ D_STR_INDIRECT " " D_STR_MODE #ifndef D_STR_ARRIS #define D_STR_ARRIS "ARRIS" #endif // D_STR_ARRIS +#ifndef D_STR_BOSCH +#define D_STR_BOSCH "BOSCH" +#endif // D_STR_BOSCH +#ifndef D_STR_BOSCH144 +#define D_STR_BOSCH144 D_STR_BOSCH "144" +#endif // D_STR_BOSCH144 #ifndef D_STR_BOSE #define D_STR_BOSE "BOSE" #endif // D_STR_BOSE @@ -715,6 +727,12 @@ D_STR_INDIRECT " " D_STR_MODE #ifndef D_STR_CARRIER_AC64 #define D_STR_CARRIER_AC64 D_STR_CARRIER_AC "64" #endif // D_STR_CARRIER_AC64 +#ifndef D_STR_CARRIER_AC128 +#define D_STR_CARRIER_AC128 D_STR_CARRIER_AC "128" +#endif // D_STR_CARRIER_AC128 +#ifndef D_STR_CLIMABUTLER +#define D_STR_CLIMABUTLER "CLIMABUTLER" +#endif // D_STR_CLIMABUTLER #ifndef D_STR_COOLIX #define D_STR_COOLIX "COOLIX" #endif // D_STR_COOLIX @@ -728,25 +746,31 @@ D_STR_INDIRECT " " D_STR_MODE #define D_STR_DAIKIN "DAIKIN" #endif // D_STR_DAIKIN #ifndef D_STR_DAIKIN128 -#define D_STR_DAIKIN128 "DAIKIN128" +#define D_STR_DAIKIN128 D_STR_DAIKIN "128" #endif // D_STR_DAIKIN128 #ifndef D_STR_DAIKIN152 -#define D_STR_DAIKIN152 "DAIKIN152" +#define D_STR_DAIKIN152 D_STR_DAIKIN "152" #endif // D_STR_DAIKIN152 #ifndef D_STR_DAIKIN160 -#define D_STR_DAIKIN160 "DAIKIN160" +#define D_STR_DAIKIN160 D_STR_DAIKIN "160" #endif // D_STR_DAIKIN160 #ifndef D_STR_DAIKIN176 -#define D_STR_DAIKIN176 "DAIKIN176" +#define D_STR_DAIKIN176 D_STR_DAIKIN "176" #endif // D_STR_DAIKIN176 #ifndef D_STR_DAIKIN2 -#define D_STR_DAIKIN2 "DAIKIN2" +#define D_STR_DAIKIN2 D_STR_DAIKIN "2" #endif // D_STR_DAIKIN2 +#ifndef D_STR_DAIKIN200 +#define D_STR_DAIKIN200 D_STR_DAIKIN "200" +#endif // D_STR_DAIKIN200 #ifndef D_STR_DAIKIN216 -#define D_STR_DAIKIN216 "DAIKIN216" +#define D_STR_DAIKIN216 D_STR_DAIKIN "216" #endif // D_STR_DAIKIN216 +#ifndef D_STR_DAIKIN312 +#define D_STR_DAIKIN312 D_STR_DAIKIN "312" +#endif // D_STR_DAIKIN312 #ifndef D_STR_DAIKIN64 -#define D_STR_DAIKIN64 "DAIKIN64" +#define D_STR_DAIKIN64 D_STR_DAIKIN "64" #endif // D_STR_DAIKIN64 #ifndef D_STR_DELONGHI_AC #define D_STR_DELONGHI_AC "DELONGHI_AC" @@ -791,28 +815,37 @@ D_STR_INDIRECT " " D_STR_MODE #define D_STR_HAIER_AC "HAIER_AC" #endif // D_STR_HAIER_AC #ifndef D_STR_HAIER_AC_YRW02 -#define D_STR_HAIER_AC_YRW02 "HAIER_AC_YRW02" +#define D_STR_HAIER_AC_YRW02 D_STR_HAIER_AC "_YRW02" #endif // D_STR_HAIER_AC_YRW02 +#ifndef D_STR_HAIER_AC160 +#define D_STR_HAIER_AC160 D_STR_HAIER_AC "160" +#endif // D_STR_HAIER_AC160 #ifndef D_STR_HAIER_AC176 -#define D_STR_HAIER_AC176 "HAIER_AC176" +#define D_STR_HAIER_AC176 D_STR_HAIER_AC "176" #endif // D_STR_HAIER_AC176 #ifndef D_STR_HITACHI_AC #define D_STR_HITACHI_AC "HITACHI_AC" #endif // D_STR_HITACHI_AC #ifndef D_STR_HITACHI_AC1 -#define D_STR_HITACHI_AC1 "HITACHI_AC1" +#define D_STR_HITACHI_AC1 D_STR_HITACHI_AC "1" #endif // D_STR_HITACHI_AC1 #ifndef D_STR_HITACHI_AC2 -#define D_STR_HITACHI_AC2 "HITACHI_AC2" +#define D_STR_HITACHI_AC2 D_STR_HITACHI_AC "2" #endif // D_STR_HITACHI_AC2 #ifndef D_STR_HITACHI_AC3 -#define D_STR_HITACHI_AC3 "HITACHI_AC3" +#define D_STR_HITACHI_AC3 D_STR_HITACHI_AC "3" #endif // D_STR_HITACHI_AC3 +#ifndef D_STR_HITACHI_AC264 +#define D_STR_HITACHI_AC264 D_STR_HITACHI_AC "264" +#endif // D_STR_HITACHI_AC264 +#ifndef D_STR_HITACHI_AC296 +#define D_STR_HITACHI_AC296 D_STR_HITACHI_AC "296" +#endif // D_STR_HITACHI_AC296 #ifndef D_STR_HITACHI_AC344 -#define D_STR_HITACHI_AC344 "HITACHI_AC344" +#define D_STR_HITACHI_AC344 D_STR_HITACHI_AC "344" #endif // D_STR_HITACHI_AC344 #ifndef D_STR_HITACHI_AC424 -#define D_STR_HITACHI_AC424 "HITACHI_AC424" +#define D_STR_HITACHI_AC424 D_STR_HITACHI_AC "424" #endif // D_STR_HITACHI_AC424 #ifndef D_STR_INAX #define D_STR_INAX "INAX" @@ -823,6 +856,9 @@ D_STR_INDIRECT " " D_STR_MODE #ifndef D_STR_KELON #define D_STR_KELON "KELON" #endif // D_STR_KELON +#ifndef D_STR_KELON168 +#define D_STR_KELON168 D_STR_KELON "168" +#endif // D_STR_KELON168 #ifndef D_STR_KELVINATOR #define D_STR_KELVINATOR "KELVINATOR" #endif // D_STR_KELVINATOR @@ -947,13 +983,16 @@ D_STR_INDIRECT " " D_STR_MODE #define D_STR_SANYO "SANYO" #endif // D_STR_SANYO #ifndef D_STR_SANYO_AC -#define D_STR_SANYO_AC "SANYO_AC" +#define D_STR_SANYO_AC D_STR_SANYO "_AC" #endif // D_STR_SANYO_AC #ifndef D_STR_SANYO_AC88 -#define D_STR_SANYO_AC88 "SANYO_AC88" +#define D_STR_SANYO_AC88 D_STR_SANYO_AC "88" #endif // D_STR_SANYO_AC88 +#ifndef D_STR_SANYO_AC152 +#define D_STR_SANYO_AC152 D_STR_SANYO_AC "152" +#endif // D_STR_SANYO_AC152 #ifndef D_STR_SANYO_LC7461 -#define D_STR_SANYO_LC7461 "SANYO_LC7461" +#define D_STR_SANYO_LC7461 D_STR_SANYO "_LC7461" #endif // D_STR_SANYO_LC7461 #ifndef D_STR_SHARP #define D_STR_SHARP "SHARP" @@ -973,6 +1012,9 @@ D_STR_INDIRECT " " D_STR_MODE #ifndef D_STR_SYMPHONY #define D_STR_SYMPHONY "SYMPHONY" #endif // D_STR_SYMPHONY +#ifndef D_STR_TCL96AC +#define D_STR_TCL96AC "TCL96AC" +#endif // D_STR_TCL96AC #ifndef D_STR_TCL112AC #define D_STR_TCL112AC "TCL112AC" #endif // D_STR_TCL112AC @@ -988,6 +1030,9 @@ D_STR_INDIRECT " " D_STR_MODE #ifndef D_STR_TOSHIBA_AC #define D_STR_TOSHIBA_AC "TOSHIBA_AC" #endif // D_STR_TOSHIBA_AC +#ifndef D_STR_TOTO +#define D_STR_TOTO "TOTO" +#endif // D_STR_TOTO #ifndef D_STR_TRANSCOLD #define D_STR_TRANSCOLD "TRANSCOLD" #endif // D_STR_TRANSCOLD diff --git a/lib/IRremoteESP8266/test/IRac_test.cpp b/lib/IRremoteESP8266/test/IRac_test.cpp index 2988e4b4b..30d9f7e4f 100644 --- a/lib/IRremoteESP8266/test/IRac_test.cpp +++ b/lib/IRremoteESP8266/test/IRac_test.cpp @@ -623,9 +623,9 @@ TEST(TestIRac, Fujitsu) { "Model: 2 (ARDB1), Id: 0, Power: On, Mode: 1 (Cool), Temp: 19C, " "Fan: 2 (Medium), Command: N/A"; std::string arrah2e_expected = - "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 19C, " - "Fan: 2 (Medium), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Sleep Timer: 03:00"; + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 10C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: On, " + "Swing: 0 (Off), Command: N/A, Sleep Timer: 03:00"; std::string arry4_expected = "Model: 5 (ARRY4), Id: 0, Power: On, Mode: 1 (Cool), Temp: 19C, " "Fan: 2 (Medium), Clean: On, Filter: On, Swing: 0 (Off), Command: N/A"; @@ -658,20 +658,21 @@ TEST(TestIRac, Fujitsu) { ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); ac._irsend.reset(); + // Try to set the device to 10C Heat mode. irac.fujitsu(&ac, ARRAH2E, // Model true, // Power - stdAc::opmode_t::kCool, // Mode + stdAc::opmode_t::kFan, // Mode (Fan needed for 10C Heat) true, // Celsius - 19, // Degrees - stdAc::fanspeed_t::kMedium, // Fan speed - stdAc::swingv_t::kOff, // Vertical swing - stdAc::swingh_t::kOff, // Horizontal swing + 19, // Degrees (Ignored in 10C Heat) + stdAc::fanspeed_t::kAuto, // Fan speed (Auto needed for 10C) + stdAc::swingv_t::kOff, // Vertical swing (Ditto) + stdAc::swingh_t::kOff, // Horizontal swing (Ditto) false, // Quiet false, // Turbo (Powerful) false, // Econo true, // Filter - true, // Clean + true, // Clean (Needed for 10C Heat) 3 * 60); // Sleep ASSERT_EQ(arrah2e_expected, ac.toString()); ac._irsend.makeDecodeResult(); @@ -944,6 +945,63 @@ TEST(TestIRac, Hitachi1) { ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); } +TEST(TestIRac, Hitachi264) { + IRHitachiAc264 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + char expected_swingon[] = + "Power: On, Mode: 6 (Heat), Temp: 25C, Fan: 4 (High), " + "Button: 19 (Power/Mode)"; + + ac.begin(); + irac.hitachi264(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 25, // Celsius + stdAc::fanspeed_t::kMax); // Fan speed + + ASSERT_EQ(expected_swingon, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HITACHI_AC264, ac._irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc264Bits, ac._irsend.capture.bits); + ASSERT_EQ(expected_swingon, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); + EXPECT_EQ(decode_type_t::HITACHI_AC264, r.protocol); + EXPECT_TRUE(r.power); + EXPECT_EQ(stdAc::opmode_t::kHeat, r.mode); + EXPECT_EQ(25, r.degrees); +} + +TEST(TestIRac, Hitachi296) { + IRHitachiAc296 ac(kGpioUnused); + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + char expected[] = + "Power: On, Mode: 6 (Heat), Temp: 20C, Fan: 2 (Low)"; + + ac.begin(); + irac.hitachi296(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 20, // Celsius + stdAc::fanspeed_t::kLow); // Fan speed + + ASSERT_EQ(expected, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HITACHI_AC296, ac._irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc296Bits, ac._irsend.capture.bits); + ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p)); + EXPECT_EQ(decode_type_t::HITACHI_AC296, r.protocol); + EXPECT_TRUE(r.power); + EXPECT_EQ(stdAc::opmode_t::kHeat, r.mode); + EXPECT_EQ(20, r.degrees); +} + TEST(TestIRac, Hitachi344) { IRHitachiAc344 ac(kGpioUnused); IRac irac(kGpioUnused); @@ -1047,7 +1105,7 @@ TEST(TestIRac, Kelvinator) { char expected[] = "Power: On, Mode: 1 (Cool), Temp: 19C, Fan: 3 (Medium), Turbo: Off, " "Quiet: Off, XFan: On, Ion: On, Light: On, " - "Swing(H): Off, Swing(V): Off"; + "Swing(H): Off, Swing(V): 0 (Off)"; ac.begin(); irac.kelvinator(&ac, @@ -1359,8 +1417,8 @@ TEST(TestIRac, Midea) { char expected[] = "Type: 1 (Command), Power: On, Mode: 1 (Dry), Celsius: On, " "Temp: 27C/80F, On Timer: Off, Off Timer: Off, Fan: 2 (Medium), " - "Sleep: On, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off"; + "Sleep: On, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -"; ac.begin(); irac.midea(&ac, @@ -1370,9 +1428,12 @@ TEST(TestIRac, Midea) { 27, // Degrees stdAc::fanspeed_t::kMedium, // Fan speed stdAc::swingv_t::kOff, // Swing(V) + false, // Silent/Quiet + false, // Previous Silent/Quiet setting false, // Turbo false, // Econo false, // Light + false, // Clean 8 * 60 + 0); // Sleep time ASSERT_EQ(expected, ac.toString()); @@ -2962,7 +3023,6 @@ TEST(TestIRac, Issue1250) { TEST(TestIRac, Issue1339) { IRac irac(kGpioUnused); stdAc::state_t to_send; - IRac::initState(&to_send); to_send.protocol = decode_type_t::SAMSUNG_AC; ASSERT_TRUE(irac.sendAc(to_send, NULL)); @@ -3027,3 +3087,20 @@ TEST(TestIRac, Issue1424) { // Confirm the state really did change. ASSERT_TRUE(IRac::cmpStates(irac.next, copy_of_next_pre_receive)); } + +// Confirm/check that the default initialisation of a state_t is as expected. +TEST(TestIRac, initState) { + IRac irac(kGpioUnused); + stdAc::state_t builtin_init{}; + stdAc::state_t custom_init; + stdAc::state_t no_init; + IRac::initState(&custom_init); + + EXPECT_FALSE(IRac::cmpStates(builtin_init, custom_init)); + builtin_init.protocol = decode_type_t::SAMSUNG_AC; + EXPECT_TRUE(IRac::cmpStates(builtin_init, custom_init)); + EXPECT_FALSE(IRac::cmpStates(no_init, custom_init)); + EXPECT_EQ(-1, builtin_init.model); + EXPECT_EQ(stdAc::swingv_t::kOff, builtin_init.swingv); + EXPECT_EQ(decode_type_t::UNKNOWN, no_init.protocol); +} diff --git a/lib/IRremoteESP8266/test/IRutils_test.cpp b/lib/IRremoteESP8266/test/IRutils_test.cpp index 73ba56999..c5a6d1339 100644 --- a/lib/IRremoteESP8266/test/IRutils_test.cpp +++ b/lib/IRremoteESP8266/test/IRutils_test.cpp @@ -780,3 +780,17 @@ TEST(TestUtils, InvertedBytePairs) { TEST(TestUtils, lowLevelSanityCheck) { ASSERT_EQ(0, irutils::lowLevelSanityCheck()); } + +TEST(TestUtils, VersionDefines) { + // String + String version_str = uint64ToString(_IRREMOTEESP8266_VERSION_MAJOR) + '.' + + uint64ToString(_IRREMOTEESP8266_VERSION_MINOR) + '.' + + uint64ToString(_IRREMOTEESP8266_VERSION_PATCH); // e.g. "2.8.1" + ASSERT_EQ(_IRREMOTEESP8266_VERSION_STR, version_str); + ASSERT_EQ(_IRREMOTEESP8266_VERSION_, version_str); + // Integer + uint64_t version_int = (_IRREMOTEESP8266_VERSION_MAJOR << 16) + + (_IRREMOTEESP8266_VERSION_MINOR << 8) + + _IRREMOTEESP8266_VERSION_PATCH; + ASSERT_EQ(_IRREMOTEESP8266_VERSION, version_int); +} diff --git a/lib/IRremoteESP8266/test/ir_Bosch_test.cpp b/lib/IRremoteESP8266/test/ir_Bosch_test.cpp new file mode 100644 index 000000000..e9b6dc9ce --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Bosch_test.cpp @@ -0,0 +1,100 @@ +// Copyright 2022 David Conran + +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + + +TEST(TestUtils, Housekeeping) { + // Bosch144 + ASSERT_EQ("BOSCH144", typeToString(decode_type_t::BOSCH144)); + ASSERT_EQ(decode_type_t::BOSCH144, strToDecodeType("BOSCH144")); + ASSERT_TRUE(hasACState(decode_type_t::BOSCH144)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::BOSCH144)); + ASSERT_EQ(kBosch144Bits, IRsend::defaultBits(decode_type_t::BOSCH144)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::BOSCH144)); +} + +// Tests for decodeBosch144(). + +// Decode normal Bosch144 messages. +TEST(TestDecodeBosch144, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1787#issuecomment-1099993189 + // Mode: Cool; Fan: 100% ; Temp: 16°C + const uint16_t rawData[299] = { + 4380, 4400, + 528, 1646, 504, 570, 504, 1646, 504, 1646, 504, 572, 502, 570, 504, 1646, + 504, 570, 504, 572, 502, 1646, 504, 570, 502, 570, 502, 1648, 502, 1646, + 502, 570, 502, 1646, 504, 572, 502, 572, 502, 1644, 504, 1646, 504, 1646, + 504, 1646, 502, 1648, 500, 1646, 504, 1646, 504, 1646, 504, 572, 502, 570, + 504, 570, 504, 570, 504, 570, 504, 570, 506, 570, 502, 572, 502, 570, 502, + 572, 502, 572, 502, 572, 502, 572, 502, 572, 500, 1648, 502, 1644, 502, + 1646, 504, 1646, 502, 1646, 504, 1646, 504, 1644, 504, 1646, + 504, 5234, + 4360, 4422, + 504, 1646, 502, 596, 478, 1670, 478, 1646, 504, 570, 504, 572, 500, 1646, + 502, 572, 502, 572, 502, 1644, 506, 570, 502, 570, 504, 1644, 506, 1644, + 502, 574, 502, 1644, 504, 570, 504, 570, 504, 1644, 504, 1646, 504, 1644, + 506, 1644, 504, 1646, 504, 1646, 504, 1644, 504, 1646, 502, 570, 504, 570, + 504, 570, 504, 570, 502, 570, 504, 570, 502, 572, 502, 570, 504, 570, 504, + 570, 504, 570, 502, 572, 502, 570, 506, 570, 504, 1646, 502, 1646, 504, + 1646, 504, 1646, 504, 1646, 502, 1644, 504, 1644, 504, 1646, + 502, 5236, + 4360, 4424, + 504, 1646, 504, 1646, 502, 572, 504, 1644, 504, 570, 504, 1646, 504, 570, + 502, 1644, 504, 570, 504, 1644, 506, 1646, 502, 572, 502, 572, 502, 1646, + 504, 570, 504, 570, 504, 570, 502, 572, 504, 570, 504, 570, 504, 570, 502, + 572, 502, 570, 504, 570, 502, 570, 504, 572, 502, 572, 502, 1646, 504, + 570, 504, 570, 504, 570, 502, 574, 502, 572, 502, 572, 502, 572, 502, 572, + 502, 572, 502, 570, 504, 572, 502, 572, 502, 572, 502, 1646, 504, 572, + 502, 570, 502, 1646, 504, 572, 504, 570, 504, 1644, + 504}; // COOLIX B23F00 + const uint8_t expectedState[kBosch144StateLength] = { + 0xB2, 0x4D, 0x3F, 0xC0, 0x00, 0xFF, + 0xB2, 0x4D, 0x3F, 0xC0, 0x00, 0xFF, + 0xD5, 0x64, 0x00, 0x10, 0x00, 0x49}; + irsend.begin(); + irsend.reset(); + + irsend.sendRaw(rawData, 299, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::BOSCH144, irsend.capture.decode_type); + EXPECT_EQ(kBosch144Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t result, prev; + ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); +} + +TEST(TestDecodeBosch144, SyntheticSelfDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + const uint8_t expectedState[kBosch144StateLength] = { + 0xB2, 0x4D, 0x3F, 0xC0, 0x00, 0xFF, + 0xB2, 0x4D, 0x3F, 0xC0, 0x00, 0xFF, + 0xD5, 0x64, 0x00, 0x10, 0x00, 0x49}; + irsend.sendBosch144(expectedState); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::BOSCH144, irsend.capture.decode_type); + EXPECT_EQ(kBosch144Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t result, prev; + ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); +} diff --git a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp index caf7e46d8..fa0f07b0c 100644 --- a/lib/IRremoteESP8266/test/ir_Carrier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Carrier_test.cpp @@ -1,8 +1,9 @@ -// Copyright 2018, 2020 David Conran +// Copyright 2018-2022 David Conran #include "ir_Carrier.h" #include "IRac.h" #include "IRrecv.h" +#include "IRrecv_test.h" #include "IRsend.h" #include "IRsend_test.h" #include "gtest/gtest.h" @@ -11,7 +12,7 @@ // Test sending typical data only. TEST(TestSendCarrierAC, SendDataOnly) { - IRsendTest irsend(0); + IRsendTest irsend(kGpioUnused); irsend.begin(); irsend.reset(); @@ -88,7 +89,7 @@ TEST(TestSendCarrierAC, SendDataOnly) { // Test sending typical data only. TEST(TestSendCarrierAC, SendWithRepeats) { - IRsendTest irsend(0); + IRsendTest irsend(kGpioUnused); irsend.begin(); irsend.reset(); @@ -156,7 +157,7 @@ TEST(TestSendCarrierAC, SendWithRepeats) { // Decode normal "synthetic" messages. TEST(TestDecodeCarrierAC, NormalDecodeWithStrict) { - IRsendTest irsend(0); + IRsendTest irsend(kGpioUnused); IRrecv irrecv(0); irsend.begin(); @@ -196,7 +197,7 @@ TEST(TestDecodeCarrierAC, NormalDecodeWithStrict) { // Decode a "real" example message. TEST(TestDecodeCarrierAC, RealExamples) { - IRsendTest irsend(0); + IRsendTest irsend(kGpioUnused); IRrecv irrecv(0); irsend.begin(); @@ -262,6 +263,16 @@ TEST(TestUtils, Housekeeping) { IRsend::defaultBits(decode_type_t::CARRIER_AC64)); ASSERT_EQ(kCarrierAc64MinRepeat, IRsend::minRepeats(decode_type_t::CARRIER_AC64)); + + // CARRIER_AC128 + ASSERT_EQ("CARRIER_AC128", typeToString(decode_type_t::CARRIER_AC128)); + ASSERT_EQ(decode_type_t::CARRIER_AC128, strToDecodeType("CARRIER_AC128")); + ASSERT_TRUE(hasACState(decode_type_t::CARRIER_AC128)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::CARRIER_AC128)); + ASSERT_EQ(kCarrierAc128Bits, + IRsend::defaultBits(decode_type_t::CARRIER_AC128)); + ASSERT_EQ(kCarrierAc128MinRepeat, + IRsend::minRepeats(decode_type_t::CARRIER_AC128)); } /// Decode a "real" example message. @@ -610,3 +621,75 @@ TEST(TestCarrierAc64Class, ReconstructKnownState) { "Sleep: On, On Timer: Off, Off Timer: Off", ac.toString()); } + +// Decode a "real" Carrier 128bit example message. +TEST(TestDecodeCarrierAC128, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + const uint8_t expected_state[kCarrierAc128StateLength] = { + 0x16, 0x22, 0x48, 0x19, 0x10, 0x10, 0x16, 0x28, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x80}; + irsend.begin(); + + irsend.reset(); + // Data from: + // https://docs.google.com/spreadsheets/d/1lqs1UDAvUauzAyoVRAGhARKPaSk3pgOO7iHFw9M5HJE/edit#gid=0&range=F3 + const uint16_t rawData[267] = { + 4594, 2614, + 336, 418, 336, 998, 330, 1002, 338, 416, 336, 998, 332, 424, 328, 426, + 336, 416, 336, 418, 334, 1000, 332, 420, 330, 424, 338, 418, 336, 994, + 334, 420, 332, 424, 328, 424, 338, 416, 336, 418, 336, 996, 332, 422, 330, + 424, 338, 992, 338, 416, 336, 998, 332, 424, 336, 418, 336, 994, 336, 998, + 332, 420, 340, 416, 338, 416, 334, 420, 334, 420, 332, 424, 328, 426, 336, + 994, 336, 418, 334, 422, 330, 422, 332, 422, 338, 416, 338, 416, 336, 418, + 332, 1000, 332, 424, 336, 416, 338, 418, 334, 420, 332, 1000, 332, 1002, + 336, 416, 338, 994, 334, 422, 330, 424, 338, 416, 336, 416, 336, 420, 332, + 422, 330, 1000, 342, 412, 338, 996, 334, 422, 332, 402, + 338, 20636, + 4604, 6724, + 9296, 4976, + 356, 426, 336, 418, 334, 420, 332, 420, 330, 424, 340, 414, 338, 416, 336, + 420, 334, 418, 334, 420, 332, 422, 340, 416, 336, 418, 336, 416, 336, 420, + 332, 420, 332, 424, 338, 416, 336, 418, 336, 994, 334, 422, 332, 422, 340, + 412, 338, 418, 334, 418, 334, 420, 332, 422, 332, 424, 340, 414, 338, 414, + 336, 420, 334, 420, 330, 424, 328, 426, 336, 416, 336, 418, 334, 420, 366, + 388, 362, 392, 358, 392, 360, 394, 368, 386, 368, 388, 364, 388, 366, 390, + 360, 394, 358, 394, 368, 388, 366, 386, 366, 390, 362, 392, 358, 394, 360, + 394, 370, 384, 366, 388, 362, 390, 362, 390, 362, 392, 360, 396, 368, 384, + 366, 390, 364, 390, 362, 392, 360, 954, + 336, 20638, + 4622}; + + irsend.sendRaw(rawData, 267, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(CARRIER_AC128, irsend.capture.decode_type); + EXPECT_EQ(kCarrierAc128Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected_state, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); +} + +// Decode a synthetic Carrier AC 128-bit message. +TEST(TestDecodeCarrierAC128, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expected_state[kCarrierAc128StateLength] = { + 0x16, 0x22, 0x48, 0x19, 0x10, 0x10, 0x16, 0x28, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x80}; + irsend.reset(); + irsend.sendCarrierAC128(expected_state); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(CARRIER_AC128, irsend.capture.decode_type); + ASSERT_EQ(kCarrierAc128Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected_state, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} diff --git a/lib/IRremoteESP8266/test/ir_ClimaButler_test.cpp b/lib/IRremoteESP8266/test/ir_ClimaButler_test.cpp new file mode 100644 index 000000000..6a9841d27 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_ClimaButler_test.cpp @@ -0,0 +1,67 @@ +// Copyright 2022 crankyoldgit + +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for decodeClimaButler(). +TEST(TestDecodeClimaButler, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + const uint16_t rawData[109] = { + 554, 3512, + 558, 1488, 580, 496, 522, 494, 546, 558, 552, 494, 548, 496, 546, 500, + 548, 558, 544, 504, 544, 502, 540, 506, 538, 570, 514, 530, 514, 532, 512, + 534, 512, 592, 512, 536, 510, 532, 510, 536, 510, 598, 510, 536, 506, 536, + 514, 534, 510, 594, 514, 534, 510, 536, 534, 510, 510, 598, 514, 534, 510, + 536, 508, 536, 534, 572, 534, 500, 512, 1536, 512, 526, 510, 1588, 512, + 1536, 510, 538, 512, 532, 510, 588, 510, 1536, 514, 532, 510, 536, 534, + 570, 512, 534, 510, 536, 514, 528, 540, 568, 540, 506, 512, 524, 510, + 1534, 516, 532, + 536, 3396, + 544}; // UNKNOWN E6CA5369 POWER OFF + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 109, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::CLIMABUTLER, irsend.capture.decode_type); + ASSERT_EQ(kClimaButlerBits, irsend.capture.bits); + EXPECT_EQ(0x8000000058802, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +TEST(TestDecodeClimaButler, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + irsend.reset(); + irsend.sendClimaButler(0x8000000058802); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::CLIMABUTLER, irsend.capture.decode_type); + ASSERT_EQ(kClimaButlerBits, irsend.capture.bits); + EXPECT_EQ(0x8000000058802, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("CLIMABUTLER", typeToString(decode_type_t::CLIMABUTLER)); + ASSERT_EQ(decode_type_t::CLIMABUTLER, strToDecodeType("CLIMABUTLER")); + ASSERT_FALSE(hasACState(decode_type_t::CLIMABUTLER)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::CLIMABUTLER)); + ASSERT_EQ(kClimaButlerBits, IRsend::defaultBits(decode_type_t::CLIMABUTLER)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::CLIMABUTLER)); +} diff --git a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp index 62e0d6b0e..ba3610bc7 100644 --- a/lib/IRremoteESP8266/test/ir_Coolix_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Coolix_test.cpp @@ -600,6 +600,41 @@ TEST(TestDecodeCoolix, RealCaptureExample) { EXPECT_EQ(0x0, irsend.capture.command); } +TEST(TestDecodeCoolix, Issue1748Example) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1748#issuecomment-1024907551 + const uint16_t powerOffRawData[199] = { + 4642, 4502, 514, 1706, 516, 624, 488, 1704, 514, 1702, 516, 624, 488, 624, + 488, 1702, 514, 626, 488, 620, 488, 1702, 490, 620, 512, 620, 488, 1728, + 488, 1704, 514, 624, 488, 1704, 514, 620, 488, 1702, 490, 1722, 488, 1724, + 514, 1728, 488, 600, 512, 1728, 490, 1706, 512, 1698, 488, 646, 486, 622, + 462, 646, 488, 624, 488, 1704, 514, 626, 488, 628, 460, 1724, 514, 1702, + 514, 1724, 462, 646, 488, 624, 488, 624, 488, 626, 486, 602, 488, 646, + 460, 648, 486, 626, 488, 1704, 486, 1724, 488, 1748, 488, 1704, 514, 1708, + 488, 5312, 4648, 4494, 488, 1704, 486, 646, 486, 1698, 512, 1700, 488, + 646, 462, 646, 486, 1728, 462, 648, 484, 622, 462, 1724, 510, 622, 488, + 626, 488, 1702, 514, 1728, 490, 626, 488, 1730, 462, 646, 488, 1704, 512, + 1724, 486, 1698, 514, 1728, 488, 626, 488, 1728, 488, 1704, 514, 1700, + 512, 620, 486, 620, 488, 620, 486, 626, 490, 1728, 488, 626, 488, 628, + 460, 1750, 488, 1728, 488, 1704, 488, 646, 488, 620, 488, 624, 488, 626, + 488, 626, 462, 646, 462, 644, 488, 626, 488, 1728, 490, 1704, 486, 1724, + 514, 1724, 488, 1728, 488}; // COOLIX48 B24D7B84E01F + + irsend.begin(); + + irsend.reset(); + + irsend.sendRaw(powerOffRawData, 199, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(COOLIX, irsend.capture.decode_type); + EXPECT_EQ(kCoolixBits, irsend.capture.bits); + EXPECT_EQ(kCoolixOff, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} // Tests to debug/fix: // https://github.com/crankyoldgit/IRremoteESP8266/issues/624 diff --git a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp index 783bd4e66..71641d4dc 100644 --- a/lib/IRremoteESP8266/test/ir_Daikin_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Daikin_test.cpp @@ -1552,6 +1552,20 @@ TEST(TestUtils, Housekeeping) { ASSERT_EQ(decode_type_t::DAIKIN64, strToDecodeType("DAIKIN64")); ASSERT_FALSE(hasACState(decode_type_t::DAIKIN64)); ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN64)); + + ASSERT_EQ("DAIKIN200", typeToString(decode_type_t::DAIKIN200)); + ASSERT_EQ(decode_type_t::DAIKIN200, strToDecodeType("DAIKIN200")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN200)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::DAIKIN200)); + ASSERT_EQ(kDaikin200Bits, IRsend::defaultBits(decode_type_t::DAIKIN200)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::DAIKIN200)); + + ASSERT_EQ("DAIKIN312", typeToString(decode_type_t::DAIKIN312)); + ASSERT_EQ(decode_type_t::DAIKIN312, strToDecodeType("DAIKIN312")); + ASSERT_TRUE(hasACState(decode_type_t::DAIKIN312)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::DAIKIN312)); + ASSERT_EQ(kDaikin312Bits, IRsend::defaultBits(decode_type_t::DAIKIN312)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::DAIKIN312)); } // https://github.com/crankyoldgit/IRremoteESP8266/issues/582#issuecomment-453863879 @@ -3879,3 +3893,228 @@ TEST(TestDaikin176Class, UnitId) { ASSERT_EQ(0, ac.getId()); EXPECT_STATE_EQ(unita, ac.getRaw(), kDaikin176Bits); } + +TEST(TestDaikin128Class, Issue1754_HeatMode) { + // Data from "MODE HEAT" + // Mesg Desc.: Power Toggle: Off, Mode: 8 (Heat), Temp: 19C, Fan: 4 (Medium), + // Powerful: Off, Quiet: Off, Swing(V): Off, Sleep: Off, Econo: Off, + // Clock: 20:22, On Timer: Off, On Timer: 10:00, Off Timer: Off, + // Off Timer: 16:30, Light Toggle: 0 (Off) + // Ref: https://docs.google.com/document/d/1bhPlwsUE4ppL1dlX6_aYmqCSxeE6HlOumYsR35S0CKA/edit#heading=h.luu34vdlzof1 + + const uint8_t heatmode[16] = { // As recorded from a real remote. + 0x16, 0x48, 0x22, 0x20, 0x10, 0x56, 0x19, 0x34, + 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B}; + + IRDaikin128 ac(kGpioUnused); + // Try to reproduce the exact same state. + ac.setClock(20 * 60 + 22); + ac.setOnTimer(10 * 60); + ac.setOnTimerEnabled(false); + ac.setOffTimer(16 * 60 + 30); + ac.setOffTimerEnabled(false); + ac.setPowerToggle(false); + ac.setTemp(19); + ac.setFan(kDaikin128FanMed); + ac.setPowerful(false); + ac.setQuiet(false); + ac.setSwingVertical(false); + ac.setSleep(false); + ac.setEcono(false); + ac.setLightToggle(0); + ac.setMode(kDaikin128Heat); + + EXPECT_EQ( + "Power Toggle: Off, Mode: 8 (Heat), Temp: 19C, Fan: 4 (Medium), " + "Powerful: Off, Quiet: Off, Swing(V): Off, Sleep: Off, Econo: Off, " + "Clock: 20:22, On Timer: Off, On Timer: 10:00, Off Timer: Off, " + "Off Timer: 16:30, Light Toggle: 0 (Off)", + ac.toString()); + // Compare the synthetic state to the captured one. + EXPECT_STATE_EQ(heatmode, ac.getRaw(), kDaikin128Bits); +} + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1802 +TEST(TestDecodeDaikin200, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + const uint16_t rawData[407] = { + 4852, 2298, + 202, 1942, 202, 870, 200, 870, 202, 868, 202, 1942, 202, 870, 202, 870, + 202, 870, 202, 870, 202, 1942, 202, 870, 202, 1942, 202, 1942, 202, 868, + 202, 1942, 202, 1942, 202, 1912, 232, 1942, 202, 1942, 202, 868, 202, + 1942, 202, 870, 202, 870, 202, 868, 202, 868, 204, 868, 202, 870, 202, + 1942, 202, 868, 204, 868, 202, 1942, 202, 870, 202, 870, 200, 870, 202, + 1942, 202, 868, 202, 870, 202, 870, 202, 870, 202, 870, 202, 870, 202, + 870, 202, 870, 202, 868, 202, 868, 204, 868, 202, 870, 202, 870, 202, 870, + 202, 1942, 202, 1942, 202, 1942, 202, 868, 202, 868, 204, 1942, 202, 870, + 202, 29468, + 4880, 2266, + 256, 1888, 230, 844, 202, 868, 202, 870, 256, 1888, 230, 842, 228, 842, + 230, 842, 256, 814, 232, 1914, 254, 816, 230, 1916, 256, 1888, 256, 814, + 256, 1890, 254, 1888, 256, 1888, 256, 1888, 256, 1888, 256, 816, 230, + 1912, 258, 814, 256, 816, 256, 816, 282, 790, 280, 790, 282, 790, 282, + 1862, 258, 814, 280, 790, 282, 1862, 280, 792, 280, 790, 282, 788, 282, + 790, 282, 790, 282, 790, 282, 790, 282, 788, 282, 790, 282, 1862, 282, + 1862, 282, 790, 282, 788, 282, 1862, 282, 1864, 280, 1862, 282, 790, 280, + 790, 282, 790, 282, 790, 282, 788, 284, 790, 282, 760, 310, 790, 282, 788, + 282, 1862, 282, 788, 282, 790, 282, 788, 282, 788, 282, 1862, 284, 788, + 282, 788, 284, 788, 282, 790, 282, 788, 284, 788, 282, 790, 282, 788, 284, + 758, 312, 788, 284, 788, 282, 788, 284, 788, 282, 788, 284, 788, 282, 788, + 284, 788, 284, 758, 314, 786, 284, 788, 284, 1860, 282, 790, 282, 790, + 282, 1862, 282, 758, 312, 762, 310, 788, 284, 788, 282, 758, 314, 758, + 314, 1862, 282, 760, 312, 1860, 284, 760, 310, 788, 282, 760, 312, 788, + 284, 760, 312, 788, 282, 790, 282, 788, 284, 788, 282, 790, 282, 788, 282, + 758, 314, 788, 282, 758, 314, 1862, 282, 758, 312, 788, 284, 758, 312, + 758, 314, 788, 282, 758, 314, 758, 312, 788, 284, 788, 284, 758, 312, 760, + 312, 762, 310, 790, 284, 788, 282, 758, 314, 758, 312, 788, 282, 760, 312, + 760, 312, 760, 312, 788, 282, 758, 314, 788, 284, 788, 284, 758, 312, 788, + 282, 790, 282, 1832, 310, 758, 316, 788, 284, 1862, 282, 1862, 282, 1832, + 310, 788, 284}; // UNKNOWN 3BFB2888 + + const uint8_t expectedState[kDaikin200StateLength] = { + 0x11, 0xDA, 0x17, 0x48, 0x04, 0x00, 0x4E, // Section 1 + 0x11, 0xDA, 0x17, 0x48, 0x00, 0x73, 0x00, 0x21, 0x00, // Section 2 + 0x00, 0x24, 0x50, 0x00, 0x20, 0x00, 0x00, 0x00, 0x72}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 407, kDaikin64Freq); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::DAIKIN200, irsend.capture.decode_type); + ASSERT_EQ(kDaikin200Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t result, prev; + ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); +} + +// Decoding a message we entirely constructed based solely on a given state. +TEST(TestDecodeDaikin200, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expectedState[kDaikin200StateLength] = { + 0x11, 0xDA, 0x17, 0x48, 0x04, 0x00, 0x4E, // Section 1 + 0x11, 0xDA, 0x17, 0x48, 0x00, 0x73, 0x00, 0x21, 0x00, // Section 2 + 0x00, 0x24, 0x50, 0x00, 0x20, 0x00, 0x00, 0x00, 0x72}; + + irsend.reset(); + irsend.sendDaikin200(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN200, irsend.capture.decode_type); + ASSERT_EQ(kDaikin200Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); +} + +/// https://github.com/crankyoldgit/IRremoteESP8266/issues/1829#issuecomment-1173077011 +TEST(TestDecodeDaikin312, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/files/9034763/on_coling_210_fan_auto.txt.out.txt + const uint16_t rawData[643] = { + 508, 390, 452, 414, 452, 414, 452, 414, 452, 414, + 452, 25104, + 3518, 1688, + 478, 1256, 478, 414, 452, 414, 452, 414, 452, 1282, 452, 414, 452, 414, + 452, 414, 452, 414, 452, 1280, 454, 414, 452, 1256, 478, 1254, 478, 414, + 452, 1280, 452, 1282, 452, 1282, 452, 1282, 478, 1256, 480, 386, 452, 414, + 452, 1282, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, + 454, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 1280, 452, 414, + 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 1282, + 452, 414, 452, 414, 452, 414, 452, 1280, 452, 1280, 452, 418, 450, 1280, + 452, 1282, 452, 414, 452, 1282, 452, 414, 452, 416, 452, 1280, 454, 414, + 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, + 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 454, 414, + 452, 414, 452, 1282, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, + 452, 414, 454, 414, 452, 414, 452, 414, 452, 414, 452, 414, 478, 388, 452, + 414, 452, 414, 452, 414, 452, 414, 452, 414, 454, 414, 452, 414, 452, 414, + 452, 414, 452, 414, 452, 414, 452, 1282, 452, 414, 452, 414, 452, 414, + 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, + 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, + 478, 388, 452, 414, 478, 388, 452, 414, 452, 414, 452, 414, 452, 414, 452, + 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, + 452, 414, 452, 414, 452, 414, 452, 414, 448, 418, 448, 418, 448, 418, 448, + 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 418, + 448, 418, 448, 418, 448, 418, 448, 418, 448, 420, 448, 418, 450, 418, 448, + 1286, 448, 1286, 448, 420, 448, 418, 448, 418, 448, 418, 448, 1284, 448, + 1286, + 448, 35512, + 3518, 1688, + 478, 1282, 452, 414, 452, 414, 452, 414, 452, 1282, 452, 414, 452, 414, + 452, 414, 452, 414, 452, 1280, 452, 414, 452, 1256, 478, 1254, 478, 414, + 452, 1280, 452, 1282, 452, 1282, 452, 1282, 452, 1280, 452, 414, 452, 414, + 452, 1280, 452, 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 418, + 448, 418, 448, 418, 452, 414, 448, 418, 448, 418, 448, 418, 448, 418, 448, + 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 1286, 452, 414, 452, + 414, 452, 1280, 452, 1282, 452, 1282, 452, 414, 452, 414, 452, 414, 452, + 1256, 478, 414, 452, 1282, 452, 414, 452, 1256, 478, 414, 452, 414, 452, + 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, + 454, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 1282, 452, 414, + 452, 1256, 478, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, + 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, 414, 452, + 414, 452, 414, 452, 414, 452, 414, 452, 1286, 448, 1286, 448, 418, 448, + 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 418, + 448, 418, 448, 1286, 448, 1286, 448, 418, 448, 418, 448, 418, 448, 418, + 448, 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 420, 448, + 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 1260, 474, + 418, 448, 1264, 470, 418, 448, 418, 448, 418, 448, 1258, 474, 1260, 474, + 418, 448, 418, 448, 418, 448, 418, 448, 418, 448, 418, 452, 414, 452, 414, + 452, 414, 452, 416, 452, 414, 448, 1264, 478, 384, 474, 418, 448, 418, + 448, 418, 448, 418, 448, 418, 448, 418, 448, 1260, 504, 362, 474, 418, + 448, 1260, 478, 388, 474}; // UNKNOWN 34EEF8FF + + const uint8_t expectedState[kDaikin312StateLength] = { + // Section 1 + 0x11, 0xDA, 0x27, 0x00, 0x02, 0x62, 0x4B, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, + // Section 2 + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x39, 0x2A, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC5, 0x00, 0x08, 0x48}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 643, kDaikin64Freq); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::DAIKIN312, irsend.capture.decode_type); + ASSERT_EQ(kDaikin312Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t result, prev; + ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); +} + +// Decoding a message we entirely constructed based solely on a given state. +TEST(TestDecodeDaikin312, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expectedState[kDaikin312StateLength] = { + // Section 1 + 0x11, 0xDA, 0x27, 0x00, 0x02, 0x62, 0x4B, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, + // Section 2 + 0x11, 0xDA, 0x27, 0x00, 0x00, 0x39, 0x2A, 0x00, 0xA0, 0x00, + 0x00, 0x06, 0x60, 0x00, 0x00, 0xC5, 0x00, 0x08, 0x48}; + + irsend.reset(); + irsend.sendDaikin312(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(DAIKIN312, irsend.capture.decode_type); + ASSERT_EQ(kDaikin312Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); +} diff --git a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp index caf4e53f1..ed5ee59f1 100644 --- a/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Fujitsu_test.cpp @@ -22,7 +22,7 @@ TEST(TestIRFujitsuACClass, GetRawDefault) { EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, Timer: Off", ac.toString()); @@ -45,7 +45,7 @@ TEST(TestIRFujitsuACClass, GetRawTurnOff) { EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 7 * 8); EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: Off, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, Timer: Off", ac.toString()); @@ -66,8 +66,8 @@ TEST(TestIRFujitsuACClass, GetRawStepHoriz) { EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, Swing: 3 (Swing(V)+Swing(H)), " - "Command: Step Swing(H), Timer: Off", + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 3 (Swing(V)+Swing(H)), Command: Step Swing(H), Timer: Off", ac.toString()); } @@ -80,8 +80,8 @@ TEST(TestIRFujitsuACClass, GetRawStepVert) { EXPECT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, Swing: 3 (Swing(V)+Swing(H)), " - "Command: Step Swing(V), Timer: Off", + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 3 (Swing(V)+Swing(H)), Command: Step Swing(V), Timer: Off", ac.toString()); ac.setModel(ARDB1); @@ -106,7 +106,7 @@ TEST(TestIRFujitsuACClass, GetRawWithSwingHoriz) { 0x90, 0x1, 0x24, 0x0, 0x0, 0x0, 0x20, 0xFB}; EXPECT_STATE_EQ(expected, ac.getRaw(), 16 * 8); EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 25C, " - "Fan: 4 (Quiet), Clean: Off, Filter: Off, " + "Fan: 4 (Quiet), Clean: Off, Filter: Off, 10C Heat: Off, " "Swing: 2 (Swing(H)), Command: N/A, Timer: Off", ac.toString()); } @@ -126,7 +126,7 @@ TEST(TestIRFujitsuACClass, GetRawWithFan) { EXPECT_STATE_EQ(expected_arrah2e, ac.getRaw(), 16 * 8); EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 20C, " - "Fan: 2 (Medium), Clean: Off, Filter: Off, " + "Fan: 2 (Medium), Clean: Off, Filter: Off, 10C Heat: Off, " "Swing: 2 (Swing(H)), Command: N/A, Timer: Off", ac.toString()); @@ -149,7 +149,7 @@ TEST(TestIRFujitsuACClass, SetRaw) { EXPECT_STATE_EQ(expected_default_arrah2e, ac.getRaw(), ac.getStateLength() * 8); EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 1 (High), Clean: Off, Filter: Off, " + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " "Swing: 3 (Swing(V)+Swing(H)), Command: N/A, " "Timer: Off", ac.toString()); @@ -318,9 +318,7 @@ TEST(TestDecodeFujitsuAC, SyntheticShortMessages) { uint8_t expected_arrah2e[7] = {0x14, 0x63, 0x0, 0x10, 0x10, 0x02, 0xFD}; EXPECT_STATE_EQ(expected_arrah2e, irsend.capture.state, irsend.capture.bits); EXPECT_EQ( - "Model: 1 (ARRAH2E), Id: 0, Power: Off, Mode: 0 (Auto), Temp: 16C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Timer: Off", + "Model: 1 (ARRAH2E), Id: 0, Power: Off, Command: N/A", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -365,7 +363,7 @@ TEST(TestDecodeFujitsuAC, SyntheticLongMessages) { ac.setRaw(irsend.capture.state, irsend.capture.bits / 8); EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); EXPECT_EQ("Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 18C, " - "Fan: 4 (Quiet), Clean: Off, Filter: Off, " + "Fan: 4 (Quiet), Clean: Off, Filter: Off, 10C Heat: Off, " "Swing: 1 (Swing(V)), Command: N/A, " "Timer: Off", ac.toString()); @@ -541,8 +539,8 @@ TEST(TestDecodeFujitsuAC, Issue414) { EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 4 (Heat), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Timer: Off", + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", ac.toString()); // Resend it using the state this time. @@ -617,9 +615,7 @@ TEST(TestIRFujitsuACClass, toCommon) { // Now test it. EXPECT_EQ( // Off mode technically has no temp, mode, fan, etc. - "Model: 1 (ARRAH2E), Id: 0, Power: Off, Mode: 0 (Auto), Temp: 16C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Timer: Off", + "Model: 1 (ARRAH2E), Id: 0, Power: 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); @@ -723,7 +719,7 @@ TEST(TestIRFujitsuACClass, OutsideQuiet) { // the option is set. Otheriwse they appear the same. EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " "Command: N/A, Timer: Off", ac.toString()); ac.setModel(fujitsu_ac_remote_model_t::ARREB1E); EXPECT_EQ( @@ -826,8 +822,8 @@ TEST(TestDecodeFujitsuAC, Issue726) { EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 24C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Timer: Off", + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", ac.toString()); } @@ -859,16 +855,16 @@ TEST(TestIRFujitsuACClass, Clean) { EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Timer: Off", + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", ac.toString()); // Now it is in ARRAH2E model mode, it shouldn't accept setting it on. ac.setClean(true); EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.getModel()); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Timer: Off", + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", ac.toString()); // But ARRY4 does. ac.setModel(fujitsu_ac_remote_model_t::ARRY4); @@ -905,8 +901,8 @@ TEST(TestIRFujitsuACClass, Filter) { EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 0 (Auto), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Timer: Off", + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, Timer: Off", ac.toString()); // Now it is in ARRAH2E model mode, it shouldn't accept setting it on. ac.setFilter(true); @@ -934,8 +930,8 @@ TEST(TestIRFujitsuACClass, Timers) { EXPECT_EQ(0, ac.getOffSleepTimer()); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "On Timer: 12:00", + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, Swing: 0 (Off), " + "Command: N/A, On Timer: 12:00", ac.toString()); const uint8_t timer_on_8h30m[kFujitsuAcStateLength] = { @@ -948,8 +944,8 @@ TEST(TestIRFujitsuACClass, Timers) { EXPECT_EQ(0, ac.getOffSleepTimer()); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "On Timer: 08:30", + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, On Timer: 08:30", ac.toString()); // TIMER OFF 11H @@ -963,8 +959,8 @@ TEST(TestIRFujitsuACClass, Timers) { EXPECT_EQ(0, ac.getOnTimer()); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Off Timer: 11:00", + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Off Timer: 11:00", ac.toString()); // TIMER OFF 0.5H @@ -978,8 +974,8 @@ TEST(TestIRFujitsuACClass, Timers) { EXPECT_EQ(0, ac.getOnTimer()); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Off Timer: 00:30", + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Off Timer: 00:30", ac.toString()); // TIMER SLEEP 3H @@ -993,8 +989,8 @@ TEST(TestIRFujitsuACClass, Timers) { EXPECT_EQ(0, ac.getOnTimer()); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Sleep Timer: 03:00", + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Sleep Timer: 03:00", ac.toString()); // Re-construct a known timer state from scratch. @@ -1011,8 +1007,8 @@ TEST(TestIRFujitsuACClass, Timers) { ac.setOffTimer(30); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Off Timer: 00:30", + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Off Timer: 00:30", ac.toString()); EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); EXPECT_STATE_EQ(timer_off_30m, ac.getRaw(), ac.getStateLength() * 8); @@ -1020,8 +1016,8 @@ TEST(TestIRFujitsuACClass, Timers) { ac.setOnTimer(12 * 60); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "On Timer: 12:00", + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, On Timer: 12:00", ac.toString()); EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); EXPECT_EQ(12 * 60, ac.getOnTimer()); @@ -1033,8 +1029,8 @@ TEST(TestIRFujitsuACClass, Timers) { ac.setSleepTimer(3 * 60); EXPECT_EQ( "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 0 (Auto), Temp: 26C, " - "Fan: 1 (High), Clean: Off, Filter: Off, Swing: 0 (Off), Command: N/A, " - "Sleep Timer: 03:00", + "Fan: 1 (High), Clean: Off, Filter: Off, 10C Heat: Off, " + "Swing: 0 (Off), Command: N/A, Sleep Timer: 03:00", ac.toString()); EXPECT_EQ(kFujitsuAcStateLength, ac.getStateLength()); EXPECT_STATE_EQ(timer_sleep_3h, ac.getRaw(), ac.getStateLength() * 8); @@ -1122,7 +1118,7 @@ TEST(TestIRFujitsuACClass, Heat10Deg) { 0x69, 0x0B, 0x00, 0x23, 0x06, 0x23, 0x20, 0xEF}; ac.setRaw(heat_on, kFujitsuAcStateLength); EXPECT_EQ( - "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 21C, " + "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 10C, " "Fan: 0 (Auto), 10C Heat: On, Swing: 0 (Off), Command: N/A, " "Outside Quiet: Off, Timer: Off", ac.toString()); @@ -1138,7 +1134,7 @@ TEST(TestIRFujitsuACClass, Heat10Deg) { ac.set10CHeat(true); EXPECT_TRUE(ac.get10CHeat()); EXPECT_EQ( - "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 21C, " + "Model: 6 (ARREW4E), Id: 1, Power: On, Mode: 3 (Fan), Temp: 10C, " "Fan: 0 (Auto), 10C Heat: On, Swing: 0 (Off), Command: N/A, " "Outside Quiet: Off, Timer: Off", ac.toString()); @@ -1271,3 +1267,182 @@ TEST(TestIRFujitsuACClass, ARREW4EShortCodes) { ASSERT_EQ(kFujitsuAcStateLengthShort, ac.getStateLength()); EXPECT_STATE_EQ(stepvert, ac.getRaw(), kFujitsuAcStateLengthShort * 8); } + +// https://github.com/crankyoldgit/IRremoteESP8266/discussions/1701#discussioncomment-1910164 +TEST(TestIRFujitsuACClass, Discussion1701) { + IRFujitsuAC ac(kGpioUnused); + IRrecv irrecv(kGpioUnused); + IRac irac(kGpioUnused); + + const String expected_raw_output = + "f38000d50" + "m3324s1574" + "m448s390m448s390m448s1182m448s390m448s1182m448s390m448s390m448s390" + "m448s1182m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s1182m448s390m448s390m448s390" + "m448s390m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182m448s1182" + "m448s1182m448s390m448s390m448s1182m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s1182m448s1182m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s1182" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s1182m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s390m448s390m448s390m448s390m448s390m448s390" + "m448s390m448s390m448s1182m448s1182m448s390m448s390m448s1182m448s390" + "m448s8100"; + const String expected_arrew4e_str = + "Model: 6 (ARREW4E), Id: 0, Power: On, Mode: 1 (Cool), Temp: 24C, " + "Fan: 1 (High), 10C Heat: Off, Swing: 0 (Off), Command: N/A, " + "Outside Quiet: Off, Timer: Off"; + const uint8_t expected_arrew4e_state[kFujitsuAcStateLength] = + {0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x31, + 0x81, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x4C}; + + // Method used in `TurnOnFujitsuAC` + ac.begin(); + ac.setModel(ARREW4E); + ac.setSwing(kFujitsuAcSwingOff); + ac.setMode(kFujitsuAcModeCool); + ac.setFanSpeed(kFujitsuAcFanHigh); + ac.setTemp(24); // 24C + ac.setCmd(kFujitsuAcCmdTurnOn); + ASSERT_EQ(expected_arrew4e_str, ac.toString()); + ac.send(); + ac._irsend.makeDecodeResult(); + // 260 = 16 (bytes) * 8 (bits) * 2 (per bit) + kHeader (2) + kFooter (2) + EXPECT_EQ(1 + 260, ac._irsend.capture.rawlen); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, ac._irsend.capture.bits); + EXPECT_EQ(expected_arrew4e_str, + IRAcUtils::resultAcToString(&ac._irsend.capture)); + EXPECT_STATE_EQ(expected_arrew4e_state, ac._irsend.capture.state, + ac._irsend.capture.bits); + EXPECT_EQ(expected_raw_output, ac._irsend.outputStr()); + + // Now try to reproduce it via the IRac class. + ac._irsend.reset(); + ac.stateReset(); + ASSERT_NE(expected_arrew4e_str, ac.toString()); + + irac.fujitsu(&ac, + ARREW4E, // Model + true, // Power + stdAc::opmode_t::kCool, // Mode + true, // Celsius + 24, // Degrees + stdAc::fanspeed_t::kHigh, // Fan speed + stdAc::swingv_t::kOff, // Vertical swing + stdAc::swingh_t::kOff, // Horizontal swing + false, // Quiet + false, // Turbo (Powerful) + false, // Econo + false, // Filter + false); // Clean + ASSERT_EQ(expected_arrew4e_str, ac.toString()); + ac._irsend.makeDecodeResult(); + // 260 = 16 (bytes) * 8 (bits) * 2 (per bit) + kHeader (2) + kFooter (2) + EXPECT_EQ(1 + 260, ac._irsend.capture.rawlen); + EXPECT_TRUE(irrecv.decode(&ac._irsend.capture)); + ASSERT_EQ(FUJITSU_AC, ac._irsend.capture.decode_type); + ASSERT_EQ(kFujitsuAcStateLength * 8, ac._irsend.capture.bits); + EXPECT_EQ(expected_arrew4e_str, + IRAcUtils::resultAcToString(&ac._irsend.capture)); + EXPECT_STATE_EQ(expected_arrew4e_state, ac._irsend.capture.state, + ac._irsend.capture.bits); + EXPECT_EQ(expected_raw_output, ac._irsend.outputStr()); + // Success. +} + +TEST(TestIRFujitsuACClass, toCommon_Issue1780HandlePrev) { + IRFujitsuAC ac(kGpioUnused); + ac.setMode(kFujitsuAcModeCool); + ac.setTemp(20); + ac.setFanSpeed(kFujitsuAcFanQuiet); + ac.setSwing(kFujitsuAcSwingBoth); + ac.on(); + ASSERT_TRUE(ac.toCommon().power); + stdAc::state_t prev = ac.toCommon(); // Copy in the state. + ac.off(); + ASSERT_FALSE(ac.toCommon().power); + ac.send(); // This should send a short code. + prev.degrees = 27; + ac.stateReset(); + IRrecv irrecv(kGpioUnused); + 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); + 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_EQ(27, ac.toCommon(&prev).degrees); + ASSERT_FALSE(ac.toCommon().quiet); + + ASSERT_EQ(stdAc::opmode_t::kAuto, ac.toCommon().mode); + ASSERT_EQ(stdAc::opmode_t::kCool, ac.toCommon(&prev).mode); + ASSERT_EQ(stdAc::fanspeed_t::kAuto, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon(&prev).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); + + stdAc::state_t result_inc_prev; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &result_inc_prev, + &prev)); + ASSERT_EQ(27, result_inc_prev.degrees); + ASSERT_EQ(stdAc::opmode_t::kCool, result_inc_prev.mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, result_inc_prev.fanspeed); +} + +TEST(TestIRFujitsuACClass, Improve10CHeat) { + IRFujitsuAC ac(kGpioUnused); + // Data from https://docs.google.com/spreadsheets/d/1RdmJdOZ3zxYlLXzluKTp4L6VVdjDXKgizwwIyTTG8MA/edit#gid=0&range=G2 + const uint8_t Arrah2u_10CHeatOn[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x41, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x20, 0x64}; + ASSERT_FALSE(ac.get10CHeat()); + ac.setRaw(Arrah2u_10CHeatOn, 16); + ASSERT_TRUE(ac.get10CHeat()); + ASSERT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 10C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: On, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + EXPECT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_TRUE(ac.get10CHeat()); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + EXPECT_EQ(kFujitsuAcMinHeat, ac.toCommon().degrees); + + ac.stateReset(); + // Data from https://docs.google.com/spreadsheets/d/1RdmJdOZ3zxYlLXzluKTp4L6VVdjDXKgizwwIyTTG8MA/edit#gid=0&range=G8 + const uint8_t Arreg1u_10CHeatOn[16] = { + 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, + 0x61, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x20, 0x44}; + ASSERT_FALSE(ac.get10CHeat()); + ac.setRaw(Arreg1u_10CHeatOn, 16); + ASSERT_TRUE(ac.get10CHeat()); + ASSERT_EQ( + "Model: 1 (ARRAH2E), Id: 0, Power: On, Mode: 3 (Fan), Temp: 10C, " + "Fan: 0 (Auto), Clean: Off, Filter: Off, 10C Heat: On, Swing: 0 (Off), " + "Command: N/A, Timer: Off", + ac.toString()); + EXPECT_EQ(decode_type_t::FUJITSU_AC, ac.toCommon().protocol); + ASSERT_TRUE(ac.get10CHeat()); + EXPECT_EQ(fujitsu_ac_remote_model_t::ARRAH2E, ac.toCommon().model); + EXPECT_EQ(kFujitsuAcMinHeat, ac.toCommon().degrees); +} diff --git a/lib/IRremoteESP8266/test/ir_Gree_test.cpp b/lib/IRremoteESP8266/test/ir_Gree_test.cpp index cb1832f61..e0160577e 100644 --- a/lib/IRremoteESP8266/test/ir_Gree_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Gree_test.cpp @@ -326,6 +326,9 @@ TEST(TestGreeClass, OperatingMode) { ac.setMode(kGreeHeat); EXPECT_EQ(kGreeHeat, ac.getMode()); + ac.setMode(kGreeEcono); + EXPECT_EQ(kGreeEcono, ac.getMode()); + ASSERT_NE(kGreeFanMax, 1); ac.setFan(kGreeFanMax); ac.setMode(kGreeDry); // Dry should lock the fan to speed 1. @@ -337,7 +340,7 @@ TEST(TestGreeClass, OperatingMode) { ac.setMode(kGreeFan); EXPECT_EQ(kGreeFan, ac.getMode()); - ac.setMode(kGreeHeat + 1); + ac.setMode(kGreeEcono + 1); EXPECT_EQ(kGreeAuto, ac.getMode()); ac.setMode(255); @@ -798,3 +801,40 @@ TEST(TestGreeClass, DisplayTempSource) { ac.setRaw(state); EXPECT_EQ(2, ac.getDisplayTempSource()); } + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("GREE", typeToString(decode_type_t::GREE)); + ASSERT_EQ(decode_type_t::GREE, strToDecodeType("GREE")); + ASSERT_TRUE(hasACState(decode_type_t::GREE)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::GREE)); + ASSERT_EQ(kGreeBits, IRsend::defaultBits(decode_type_t::GREE)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::GREE)); + ASSERT_EQ(gree_ac_remote_model_t::YAW1F, IRac::strToModel("YAW1F")); + ASSERT_EQ(irutils::modelToStr(decode_type_t::GREE, + gree_ac_remote_model_t::YAW1F), "YAW1F"); + ASSERT_EQ(gree_ac_remote_model_t::YBOFB, IRac::strToModel("YBOFB")); + ASSERT_EQ(irutils::modelToStr(decode_type_t::GREE, + gree_ac_remote_model_t::YBOFB), "YBOFB"); + ASSERT_EQ(gree_ac_remote_model_t::YX1FSF, IRac::strToModel("YX1FSF")); + ASSERT_EQ(irutils::modelToStr(decode_type_t::GREE, + gree_ac_remote_model_t::YX1FSF), "YX1FSF"); +} + +TEST(TestGreeClass, Issue1821EnergySaver) { + IRGreeAC ac(kGpioUnused); + ac.begin(); + + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1821#issue-1271458457 + const uint8_t energy[8] = {0x1D, 0x09, 0x60, 0x58, 0x00, 0x20, 0x00, 0xA0}; + + ac.setRaw(energy); + EXPECT_EQ(kGreeEcono, ac.getMode()); + EXPECT_TRUE(ac.getEcono()); + EXPECT_EQ(gree_ac_remote_model_t::YX1FSF, ac.getModel()); + EXPECT_EQ( + "Model: 3 (YX1FSF), Power: On, Mode: 5 (Econo), Temp: 77F, Fan: 1 (Low), " + "Turbo: Off, Econo: Off, IFeel: Off, WiFi: Off, XFan: Off, Light: On, " + "Sleep: Off, Swing(V) Mode: Manual, Swing(V): 0 (Last), " + "Swing(H): 0 (Off), Timer: Off, Display Temp: 0 (Off)", + ac.toString()); +} diff --git a/lib/IRremoteESP8266/test/ir_Haier_test.cpp b/lib/IRremoteESP8266/test/ir_Haier_test.cpp index b60a08f23..57c813c0d 100644 --- a/lib/IRremoteESP8266/test/ir_Haier_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Haier_test.cpp @@ -1357,6 +1357,13 @@ TEST(TestUtils, Housekeeping) { ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::HAIER_AC176)); ASSERT_EQ(kHaierAC176Bits, IRsend::defaultBits(decode_type_t::HAIER_AC176)); ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::HAIER_AC176)); + + ASSERT_EQ("HAIER_AC160", typeToString(decode_type_t::HAIER_AC160)); + ASSERT_EQ(decode_type_t::HAIER_AC160, strToDecodeType("HAIER_AC160")); + ASSERT_TRUE(hasACState(decode_type_t::HAIER_AC160)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::HAIER_AC160)); + ASSERT_EQ(kHaierAC160Bits, IRsend::defaultBits(decode_type_t::HAIER_AC160)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::HAIER_AC160)); } TEST(TestHaierAC176Class, BuildKnownState) { @@ -1535,3 +1542,79 @@ TEST(TestHaierAC176Class, Models) { "Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off", ac.toString()); } + +TEST(TestDecodeHaierAC160, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1804#issue-1236115063 + const uint16_t rawData[325] = { + 3078, 3002, + 3058, 4338, + 590, 1612, 588, 516, 584, 1588, 586, 542, 560, 540, 560, 1590, 584, 1618, + 584, 542, 558, 1592, 584, 542, 582, 1594, 584, 542, 582, 1592, 560, 1642, + 560, 516, 558, 544, 558, 542, 558, 542, 582, 518, 558, 542, 558, 516, 558, + 542, 558, 542, 582, 520, 558, 542, 558, 542, 564, 510, 608, 492, 558, 542, + 582, 520, 558, 542, 558, 544, 582, 492, 608, 1594, 558, 544, 558, 542, + 582, 494, 608, 494, 558, 544, 558, 542, 558, 544, 558, 1616, 582, 1620, + 560, 542, 558, 542, 582, 494, 558, 542, 558, 542, 558, 544, 556, 542, 582, + 520, 580, 494, 582, 520, 582, 520, 558, 542, 558, 542, 582, 520, 582, 492, + 608, 1592, 582, 520, 558, 544, 558, 516, 584, 518, 558, 542, 582, 520, + 580, 520, 582, 520, 580, 492, 584, 516, 556, 544, 582, 518, 580, 520, 580, + 520, 580, 494, 606, 494, 580, 520, 580, 520, 582, 520, 580, 520, 556, 518, + 608, 492, 580, 520, 556, 544, 580, 520, 580, 520, 580, 494, 606, 494, 580, + 520, 580, 520, 580, 520, 580, 520, 580, 494, 606, 496, 578, 522, 580, 520, + 580, 520, 580, 520, 580, 494, 606, 494, 580, 520, 580, 520, 580, 1594, + 608, 494, 580, 1620, 580, 522, 580, 520, 580, 494, 578, 1620, 582, 520, + 580, 1596, 582, 1620, 582, 1594, 608, 1594, 582, 522, 580, 1594, 582, + 1620, 582, 520, 580, 1596, 582, 520, 580, 1620, 582, 494, 606, 496, 580, + 522, 580, 522, 580, 520, 580, 520, 580, 496, 604, 496, 580, 520, 580, + 1620, 582, 1594, 582, 522, 578, 522, 580, 522, 578, 522, 580, 496, 580, + 522, 578, 522, 578, 522, 578, 524, 578, 522, 578, 496, 578, 522, 578, 522, + 578, 546, 554, 522, 578, 522, 578, 520, 556, 546, 554, 546, 554, 546, 556, + 544, 556, 546, 554, 520, 554, 544, 556, 1620, 582, 544, 556, 1594, 580, + 546, 556, 1594, 580}; // UNKNOWN B6B57D85 + const uint8_t expectedState[kHaierAC160StateLength] = { + 0xA6, 0xAC, 0x00, 0x00, 0x40, 0x60, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x17, 0xB5, 0x00, 0x60, 0x00, 0x00, 0x15}; + + irsend.sendRaw(rawData, 325, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(HAIER_AC160, irsend.capture.decode_type); + ASSERT_EQ(kHaierAC160Bits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t result, prev; + ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); +} + +// Decoding a message we entirely constructed based solely on a given state. +TEST(TestDecodeHaierAC160, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expectedState[kHaierAC160StateLength] = { + 0xA6, 0xAC, 0x00, 0x00, 0x40, 0x60, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x17, 0xB5, 0x00, 0x60, 0x00, 0x00, 0x15}; + + irsend.reset(); + irsend.sendHaierAC160(expectedState); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(HAIER_AC160, irsend.capture.decode_type); + ASSERT_EQ(kHaierAC160Bits, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t result, prev; + ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); +} diff --git a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp index 2bbbd29e9..51664688a 100644 --- a/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Hitachi_test.cpp @@ -191,7 +191,7 @@ TEST(TestSendHitachiAC, SendUnexpectedSizes) { // Tests for IRHitachiAc class. TEST(TestIRHitachiAcClass, SetAndGetPower) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.on(); EXPECT_TRUE(ac.getPower()); ac.off(); @@ -203,7 +203,7 @@ TEST(TestIRHitachiAcClass, SetAndGetPower) { } TEST(TestIRHitachiAcClass, SetAndGetSwing) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setSwingVertical(true); ac.setSwingHorizontal(true); EXPECT_TRUE(ac.getSwingVertical()); @@ -222,7 +222,7 @@ TEST(TestIRHitachiAcClass, SetAndGetSwing) { } TEST(TestIRHitachiAcClass, SetAndGetTemp) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setTemp(25); EXPECT_EQ(25, ac.getTemp()); ac.setTemp(kHitachiAcMinTemp); @@ -238,7 +238,7 @@ TEST(TestIRHitachiAcClass, SetAndGetTemp) { } TEST(TestIRHitachiAcClass, SetAndGetMode) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setMode(kHitachiAcCool); ac.setFan(kHitachiAcFanAuto); EXPECT_EQ(kHitachiAcCool, ac.getMode()); @@ -261,7 +261,7 @@ TEST(TestIRHitachiAcClass, SetAndGetMode) { } TEST(TestIRHitachiAcClass, SetAndGetFan) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setMode(kHitachiAcCool); // All fan options are available in this mode. ac.setFan(kHitachiAcFanAuto); EXPECT_EQ(kHitachiAcFanAuto, ac.getFan()); @@ -293,7 +293,7 @@ TEST(TestIRHitachiAcClass, SetAndGetFan) { } TEST(TestIRHitachiAcClass, HumanReadable) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setMode(kHitachiAcHeat); ac.setTemp(kHitachiAcMaxTemp); @@ -316,7 +316,7 @@ TEST(TestIRHitachiAcClass, HumanReadable) { } TEST(TestIRHitachiAcClass, ChecksumCalculation) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); const uint8_t originalstate[kHitachiAcStateLength] = { 0x80, 0x08, 0x0C, 0x02, 0xFD, 0x80, 0x7F, 0x88, 0x48, 0x80, @@ -426,7 +426,7 @@ TEST(TestDecodeHitachiAC, NormalRealExample1) { EXPECT_EQ(HITACHI_AC, irsend.capture.decode_type); ASSERT_EQ(kHitachiAcBits, irsend.capture.bits); EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, kHitachiAcBits); - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 4 (Cool), Temp: 16C, Fan: 1 (Auto), " @@ -494,7 +494,7 @@ TEST(TestDecodeHitachiAC, NormalRealExample2) { EXPECT_EQ(HITACHI_AC, irsend.capture.decode_type); ASSERT_EQ(kHitachiAcBits, irsend.capture.bits); EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, kHitachiAcBits); - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 3 (Heat), Temp: 32C, Fan: 5 (High), " @@ -777,7 +777,7 @@ TEST(TestDecodeHitachiAC2, NormalRealExample) { } TEST(TestIRHitachiAcClass, toCommon) { - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setPower(true); ac.setMode(kHitachiAcCool); ac.setTemp(20); @@ -856,6 +856,24 @@ TEST(TestUtils, Housekeeping) { IRsend::defaultBits(decode_type_t::HITACHI_AC344)); ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::HITACHI_AC344)); + + ASSERT_EQ("HITACHI_AC264", typeToString(decode_type_t::HITACHI_AC264)); + ASSERT_EQ(decode_type_t::HITACHI_AC264, strToDecodeType("HITACHI_AC264")); + ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC264)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC264)); + ASSERT_EQ(kHitachiAc264Bits, + IRsend::defaultBits(decode_type_t::HITACHI_AC264)); + ASSERT_EQ(kNoRepeat, + IRsend::minRepeats(decode_type_t::HITACHI_AC264)); + + ASSERT_EQ("HITACHI_AC296", typeToString(decode_type_t::HITACHI_AC296)); + ASSERT_EQ(decode_type_t::HITACHI_AC296, strToDecodeType("HITACHI_AC296")); + ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC296)); + ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC296)); + ASSERT_EQ(kHitachiAc296Bits, + IRsend::defaultBits(decode_type_t::HITACHI_AC296)); + ASSERT_EQ(kNoRepeat, + IRsend::minRepeats(decode_type_t::HITACHI_AC296)); } // Decode a 'real' HitachiAc424 message. @@ -945,7 +963,7 @@ TEST(TestDecodeHitachiAc424, RealExample) { EXPECT_EQ(HITACHI_AC424, irsend.capture.decode_type); ASSERT_EQ(kHitachiAc424Bits, irsend.capture.bits); EXPECT_STATE_EQ(expected, irsend.capture.state, kHitachiAc424Bits); - IRHitachiAc ac(0); + IRHitachiAc ac(kGpioUnused); ac.setRaw(irsend.capture.state); EXPECT_EQ( "Power: On, Mode: 3 (Cool), Temp: 23C, Fan: 5 (Auto), " @@ -979,7 +997,7 @@ TEST(TestDecodeHitachiAc424, SyntheticExample) { // Tests for IRHitachiAc424 class. TEST(TestIRHitachiAc424Class, SetInvertedStates) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); uint8_t raw[kHitachiAc424StateLength] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1001,7 +1019,7 @@ TEST(TestIRHitachiAc424Class, SetInvertedStates) { } TEST(TestIRHitachiAc424Class, SetAndGetPower) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.on(); EXPECT_TRUE(ac.getPower()); ac.off(); @@ -1015,7 +1033,7 @@ TEST(TestIRHitachiAc424Class, SetAndGetPower) { } TEST(TestIRHitachiAc424Class, SetAndGetTemp) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.setTemp(25); EXPECT_EQ(25, ac.getTemp()); ac.setTemp(kHitachiAc424MinTemp); @@ -1029,7 +1047,7 @@ TEST(TestIRHitachiAc424Class, SetAndGetTemp) { } TEST(TestIRHitachiAc424Class, SetAndGetMode) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.setMode(kHitachiAc424Cool); ac.setFan(kHitachiAc424FanAuto); ac.setTemp(25); @@ -1049,7 +1067,7 @@ TEST(TestIRHitachiAc424Class, SetAndGetMode) { } TEST(TestIRHitachiAc424Class, SetAndGetFan) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.setMode(kHitachiAc424Cool); // All fan options are available in this mode. ac.setFan(kHitachiAc424FanAuto); EXPECT_EQ(kHitachiAc424FanAuto, ac.getFan()); @@ -1094,7 +1112,7 @@ TEST(TestIRHitachiAc424Class, SetAndGetFan) { TEST(TestIRHitachiAc424Class, SetAndGetButton) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.on(); EXPECT_EQ(ac.getButton(), kHitachiAc424ButtonPowerMode); ac.setButton(kHitachiAc424ButtonTempUp); @@ -1104,7 +1122,7 @@ TEST(TestIRHitachiAc424Class, SetAndGetButton) { } TEST(TestIRHitachiAc424Class, ToggleSwingVertical) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.on(); EXPECT_EQ(ac.getButton(), kHitachiAc424ButtonPowerMode); ac.setSwingVToggle(true); @@ -1116,7 +1134,7 @@ TEST(TestIRHitachiAc424Class, ToggleSwingVertical) { } TEST(TestIRHitachiAc424Class, HumanReadable) { - IRHitachiAc424 ac(0); + IRHitachiAc424 ac(kGpioUnused); ac.setMode(kHitachiAc424Heat); ac.setTemp(kHitachiAc424MaxTemp); @@ -1984,3 +2002,410 @@ TEST(TestIRHitachiAc344Class, SwingV) { ac.setSwingV(false); EXPECT_FALSE(ac.getSwingV()); } + +// Decode a 'real' HitachiAc264 message. +TEST(TestDecodeHitachiAc264, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expected[kHitachiAc264StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xCC, 0x33, 0x92, + 0x6D, 0x13, 0xEC, 0x6C, 0x93, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x16, 0xE9, 0xC1, 0x3E, 0x00, + 0xFF, 0x00, 0xFF}; + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1729#issuecomment-1006938712 + const uint16_t rawData[531] = { + 3392, 1752, 372, 1356, 344, 504, 344, 504, 344, 480, 370, 480, 370, 478, + 370, 478, 372, 480, 370, 478, 370, 480, 370, 478, 370, 480, 370, 1330, + 370, 478, 370, 480, 370, 478, 370, 478, 370, 480, 370, 478, 370, 480, 370, + 504, 346, 504, 344, 480, 370, 504, 346, 478, 370, 478, 370, 478, 372, 480, + 370, 480, 370, 504, 346, 1330, 370, 480, 370, 1356, 346, 1330, 370, 1354, + 346, 1328, 370, 1330, 370, 1328, 370, 478, 372, 1330, 370, 1328, 370, + 1356, 346, 1354, 344, 1328, 372, 1354, 344, 1356, 344, 1330, 370, 1328, + 372, 480, 370, 478, 370, 504, 346, 478, 372, 476, 372, 504, 346, 480, 370, + 504, 344, 478, 372, 504, 344, 1328, 372, 1328, 372, 478, 370, 504, 346, + 1330, 370, 1328, 372, 1328, 370, 1356, 344, 478, 370, 478, 370, 1356, 344, + 1328, 372, 480, 370, 478, 372, 504, 346, 1330, 370, 478, 370, 480, 370, + 1330, 370, 478, 370, 480, 370, 1328, 370, 1330, 370, 480, 372, 1352, 346, + 1328, 372, 504, 346, 1328, 370, 1328, 370, 480, 370, 1328, 372, 1328, 370, + 504, 344, 478, 372, 1330, 370, 478, 370, 480, 370, 480, 370, 478, 370, + 480, 370, 1356, 346, 1328, 370, 480, 370, 1330, 370, 1330, 370, 1330, 370, + 478, 370, 506, 344, 1328, 372, 1328, 372, 478, 372, 1330, 370, 1328, 372, + 478, 370, 1330, 370, 1328, 372, 504, 344, 480, 370, 1330, 370, 504, 344, + 504, 346, 1354, 346, 504, 346, 478, 372, 478, 370, 480, 370, 478, 370, + 492, 358, 478, 370, 478, 370, 1328, 372, 1330, 370, 1328, 372, 1354, 346, + 1328, 372, 1328, 370, 1330, 370, 1328, 372, 476, 372, 504, 346, 478, 372, + 480, 370, 480, 370, 504, 344, 478, 370, 480, 370, 1328, 370, 1330, 372, + 1328, 370, 1330, 370, 1330, 370, 1330, 368, 1330, 370, 1330, 370, 480, + 370, 480, 370, 478, 370, 504, 344, 480, 370, 478, 370, 504, 344, 478, 372, + 1328, 370, 1330, 370, 1330, 370, 1330, 370, 1328, 372, 1356, 344, 1330, + 370, 1330, 370, 480, 370, 478, 370, 482, 368, 480, 370, 480, 370, 480, + 370, 480, 370, 480, 370, 1330, 370, 1354, 346, 1330, 370, 1354, 346, 1330, + 370, 1330, 370, 1330, 370, 1330, 370, 504, 346, 480, 370, 478, 372, 478, + 372, 504, 344, 480, 370, 480, 370, 504, 344, 1330, 370, 1328, 370, 1330, + 372, 1328, 370, 1354, 346, 1328, 370, 1330, 370, 1330, 370, 478, 370, + 1330, 370, 1328, 372, 480, 370, 1330, 370, 480, 370, 480, 370, 480, 370, + 1356, 344, 478, 370, 506, 344, 1330, 370, 478, 370, 1330, 372, 1354, 346, + 1354, 346, 1328, 370, 478, 372, 478, 370, 480, 370, 504, 346, 480, 370, + 1328, 370, 1330, 370, 478, 370, 1330, 370, 1328, 370, 1330, 372, 1354, + 344, 1328, 372, 504, 346, 478, 370, 504, 346, 504, 346, 478, 370, 478, + 370, 482, 368, 480, 370, 478, 370, 480, 370, 1328, 372, 1328, 370, 1330, + 370, 1328, 370, 1328, 370, 1330, 370, 1354, 346, 1328, 372, 478, 370, 478, + 370, 478, 372, 478, 372, 478, 372, 478, 372, 478, 370, 478, 372, 1330, + 370, 1328, 370, 1328, 372, 1328, 372, 1330, 444, 1256, 370, 1330, 370, + 1330, 442}; + + irsend.reset(); + irsend.sendRaw(rawData, 531, kHitachiAcFreq); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC264, irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc264Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, kHitachiAc264Bits); + EXPECT_EQ( + "Power: Off, Mode: 6 (Heat), Temp: 27C, Fan: 1 (Min), " + "Button: 19 (Power/Mode)", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +// Decode a 'Synthetic' HitachiAc264 message. +TEST(TestDecodeHitachiAc264, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1729#issuecomment-1006938712 + const uint8_t expected[kHitachiAc264StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xCC, 0x33, 0x92, + 0x6D, 0x13, 0xEC, 0x6C, 0x93, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x16, 0xE9, 0xC1, 0x3E, 0x00, + 0xFF, 0x00, 0xFF}; + + irsend.reset(); + irsend.sendHitachiAc264(expected); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC264, irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc264Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, kHitachiAc264Bits); + EXPECT_EQ( + "Power: Off, Mode: 6 (Heat), Temp: 27C, Fan: 1 (Min), " + "Button: 19 (Power/Mode)", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +TEST(TestIRHitachiAc264Class, ConstructKnownState) { + IRHitachiAc264 ac(kGpioUnused); + // hot/power off/ fan 1 /27°C + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1729#issuecomment-1006938712 + const uint8_t expected[kHitachiAc264StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xCC, 0x33, 0x92, + 0x6D, 0x13, 0xEC, 0x6C, 0x93, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x16, 0xE9, 0xC1, 0x3E, 0x00, + 0xFF, 0x00, 0xFF}; + ac.stateReset(); + ac.setMode(kHitachiAc264Heat); + ac.setFan(kHitachiAc264FanMin); + ac.setTemp(27); + ac.setPower(false); + EXPECT_EQ( + "Power: Off, Mode: 6 (Heat), Temp: 27C, Fan: 1 (Min), " + "Button: 19 (Power/Mode)", + ac.toString()); + EXPECT_STATE_EQ(expected, ac.getRaw(), kHitachiAc264Bits); +} + +TEST(TestIRHitachiAc264Class, Issue1729_PowerOntoOff) { + IRHitachiAc264 ac(kGpioUnused); + + const String on_str = "Power: On, Mode: 6 (Heat), Temp: 25C, Fan: 5 (Auto), " + "Button: 19 (Power/Mode)"; + const String off_str = "Power: Off, Mode: 6 (Heat), Temp: 25C, " + "Fan: 5 (Auto), Button: 19 (Power/Mode)"; + const uint8_t off_state[kHitachiAc264StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xCC, 0x33, 0x92, + 0x6D, 0x13, 0xEC, 0x64, 0x9B, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x56, 0xA9, 0xC1, 0x3E, 0x00, + 0xFF, 0x00, 0xFF}; + // Simulate at the `IRHitachiAc264` class level. + ac.stateReset(); + // Create an On state. + ac.setMode(kHitachiAc264Heat); + ac.setTemp(25); + ac.setFan(kHitachiAc264FanAuto); + ac.setPower(true); + EXPECT_EQ(on_str, ac.toString()); + ac.send(); // Send it + EXPECT_EQ(on_str, ac.toString()); // Check it is still the same. + + // Now construct the "Off" state. + ac.setMode(kHitachiAc264Heat); + ac.setTemp(25); + ac.setFan(kHitachiAc264FanAuto); + ac.setPower(false); + + EXPECT_EQ(off_str, ac.toString()); + EXPECT_STATE_EQ(off_state, ac.getRaw(), kHitachiAc264Bits); + ac.send(); // Send it + EXPECT_EQ(off_str, ac.toString()); // Check it is still the same "off" state. + EXPECT_STATE_EQ(off_state, ac.getRaw(), kHitachiAc264Bits); + + // Simulate at the `IRac` class level. + IRac irac(kGpioUnused); + IRrecv capture(kGpioUnused); + + ac.stateReset(); + ac._irsend.reset(); + + // Create a power on state & send it. + irac.hitachi264(&ac, + true, // Power + stdAc::opmode_t::kHeat, // Mode + 25, // Celsius + stdAc::fanspeed_t::kAuto); // Fan speed + + ASSERT_EQ(on_str, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HITACHI_AC264, ac._irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc264Bits, ac._irsend.capture.bits); + ASSERT_EQ(on_str, IRAcUtils::resultAcToString(&ac._irsend.capture)); + stdAc::state_t on, prev; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &on, &prev)); + EXPECT_EQ(decode_type_t::HITACHI_AC264, on.protocol); + EXPECT_TRUE(on.power); + EXPECT_EQ(stdAc::opmode_t::kHeat, on.mode); + EXPECT_EQ(25, on.degrees); + + ac.stateReset(); + ac._irsend.reset(); + // Create a power off state & send it. + prev = on; + irac.hitachi264(&ac, + false, // Power + stdAc::opmode_t::kHeat, // Mode + 25, // Celsius + stdAc::fanspeed_t::kAuto); // Fan speed + + ASSERT_EQ(off_str, ac.toString()); + ac._irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&ac._irsend.capture)); + ASSERT_EQ(HITACHI_AC264, ac._irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc264Bits, ac._irsend.capture.bits); + ASSERT_EQ(off_str, IRAcUtils::resultAcToString(&ac._irsend.capture)); + EXPECT_STATE_EQ(off_state, ac._irsend.capture.state, ac._irsend.capture.bits); + stdAc::state_t off; + ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &off, &prev)); + EXPECT_EQ(decode_type_t::HITACHI_AC264, off.protocol); + EXPECT_FALSE(off.power); + EXPECT_EQ(stdAc::opmode_t::kHeat, off.mode); + EXPECT_EQ(25, off.degrees); + + // Verify that handleToggles() isn't screwing anything up. + stdAc::state_t toggle_state; + toggle_state = irac.handleToggles(on, &prev); + EXPECT_FALSE(irac.cmpStates(on, prev)); // They are the same. + EXPECT_FALSE(irac.cmpStates(on, toggle_state)); // They are the same. + toggle_state = irac.handleToggles(off, &on); + EXPECT_TRUE(irac.cmpStates(off, on)); // They are different. + EXPECT_FALSE(irac.cmpStates(off, toggle_state)); // They are the same. + EXPECT_EQ(off.protocol, decode_type_t::HITACHI_AC264); + EXPECT_FALSE(off.power); + // That confirms handleToggles() isn't causing any grief. + + // Check that IRac copies & modifies the states as we expect. + irac.next = on; // Copy in the "on" state we constructed earlier. + irac.sendAc(); // Send it. + irac.markAsSent(); // Confirm it was sent. + EXPECT_FALSE(irac.cmpStates(on, irac.getStatePrev())); // They are the same. + EXPECT_FALSE(irac.cmpStates(on, irac.getState())); // They are the same. + + irac.next.power = false; // Turn off the power. + EXPECT_TRUE(irac.cmpStates(on, irac.getState())); // They are the different. + EXPECT_FALSE(off.power); + EXPECT_FALSE(irac.cmpStates(off, irac.getState())); // They are the same. + EXPECT_FALSE(irac.cmpStates(off, irac.next)); // They are the same. + irac.sendAc(); // Send it. + irac.markAsSent(); // Confirm it was sent. + EXPECT_FALSE(irac.cmpStates(off, irac.getStatePrev())); // They are the same. + EXPECT_FALSE(irac.cmpStates(off, irac.getState())); // They are the same. + EXPECT_FALSE(irac.cmpStates(off, irac.next)); // They are the same. + EXPECT_FALSE(irac.getStatePrev().power); + EXPECT_FALSE(irac.getState().power); + EXPECT_FALSE(irac.next.power); +} + +// Decode a 'real' HitachiAc296 message. +TEST(TestDecodeHitachiAc296, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + const uint8_t expected[kHitachiAc296StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xCC, 0x33, 0x92, + 0x6D, 0x44, 0xBB, 0x04, 0xFB, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x57, 0xA8, 0xF1, 0x0E, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x03, 0xFC}; + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/pull/1758#issuecomment-1066017940 + // Power: ON, Temp: -- (not used in auto mode), Fan: Auto, Mode: Auto + const uint16_t rawData[595] = { + 3298, 1712, 358, 1278, 362, 516, 356, 518, 358, 514, 358, 518, 358, 516, + 360, 516, 358, 520, 362, 514, 358, 516, 360, 514, 362, 514, 360, 1280, + 358, 518, 358, 514, 362, 518, 360, 516, 358, 516, 358, 516, 360, 516, 358, + 516, 358, 518, 358, 516, 360, 518, 362, 516, 358, 514, 360, 514, 360, 516, + 360, 516, 358, 516, 362, 1280, 356, 520, 360, 1276, 362, 1276, 360, 1282, + 360, 1280, 356, 1280, 358, 1278, 362, 514, 362, 1280, 360, 1280, 360, + 1278, 358, 1280, 358, 1280, 358, 1278, 360, 1280, 358, 1278, 360, 1282, + 360, 514, 360, 514, 362, 516, 356, 516, 362, 514, 360, 516, 358, 516, 358, + 520, 360, 516, 358, 516, 362, 1276, 360, 1278, 360, 514, 358, 516, 360, + 1278, 360, 1282, 360, 1276, 362, 1278, 360, 514, 358, 518, 358, 1278, 360, + 1278, 360, 514, 360, 518, 360, 516, 358, 1280, 358, 516, 360, 516, 360, + 1278, 360, 518, 356, 516, 360, 1282, 360, 1278, 362, 514, 360, 1278, 360, + 1278, 360, 516, 358, 1278, 360, 1280, 358, 520, 358, 516, 358, 516, 360, + 1276, 360, 516, 358, 514, 360, 514, 364, 1276, 358, 522, 358, 1278, 360, + 1280, 358, 516, 360, 1278, 360, 1276, 360, 1278, 362, 514, 358, 1286, 358, + 514, 358, 514, 362, 1278, 360, 514, 360, 518, 358, 516, 358, 518, 358, + 518, 360, 1276, 360, 1278, 362, 514, 360, 1276, 360, 1276, 362, 1280, 358, + 1276, 360, 1284, 358, 516, 358, 516, 360, 516, 360, 514, 360, 516, 358, + 516, 360, 518, 356, 518, 358, 1280, 358, 1278, 362, 1276, 360, 1280, 360, + 1276, 360, 1278, 362, 1278, 358, 1282, 358, 516, 358, 518, 358, 514, 360, + 516, 358, 516, 360, 514, 360, 514, 358, 520, 360, 1278, 360, 1280, 358, + 1280, 360, 1278, 360, 1278, 358, 1278, 360, 1278, 362, 1282, 360, 516, + 360, 516, 358, 516, 360, 518, 356, 516, 358, 516, 362, 514, 358, 520, 358, + 1280, 358, 1278, 362, 1276, 362, 1276, 360, 1276, 360, 1278, 360, 1278, + 360, 1282, 360, 514, 362, 514, 360, 516, 360, 514, 360, 516, 360, 516, + 360, 516, 358, 520, 362, 1278, 358, 1278, 362, 1276, 360, 1280, 358, 1276, + 362, 1276, 362, 1278, 360, 1284, 358, 516, 358, 514, 360, 516, 360, 514, + 360, 516, 360, 512, 362, 514, 360, 518, 360, 1276, 362, 1276, 362, 1278, + 358, 1280, 360, 1278, 360, 1278, 360, 1276, 362, 1282, 360, 1278, 362, + 1278, 362, 1276, 362, 512, 360, 1278, 362, 514, 360, 1278, 358, 520, 358, + 516, 360, 514, 360, 516, 358, 1278, 362, 514, 360, 1278, 358, 516, 362, + 1282, 360, 1276, 360, 516, 360, 516, 358, 518, 358, 1276, 362, 1278, 360, + 1278, 360, 1284, 358, 516, 360, 1278, 362, 1276, 360, 1278, 360, 516, 360, + 512, 362, 512, 362, 518, 362, 514, 362, 514, 358, 514, 360, 514, 360, 518, + 358, 514, 358, 516, 360, 518, 360, 1278, 360, 1278, 360, 1276, 362, 1276, + 362, 1276, 362, 1278, 358, 1278, 362, 1284, 358, 514, 362, 512, 360, 514, + 362, 516, 360, 514, 360, 514, 360, 512, 362, 520, 360, 1276, 362, 1278, + 362, 1276, 364, 1274, 360, 1278, 362, 1278, 360, 1278, 360, 1282, 362, + 512, 360, 514, 362, 512, 362, 514, 360, 516, 362, 512, 362, 516, 360, 518, + 360, 1278, 360, 1276, 362, 1278, 360, 1274, 364, 1276, 362, 1276, 360, + 1278, 360, 1280, 362, 1276, 362, 1276, 364, 512, 360, 514, 362, 512, 362, + 512, 362, 512, 362, 520, 358, 516, 360, 514, 360, 1276, 360, 1276, 362, + 1276, 362, 1278, 360, 1278, 360, 1280, 362}; // HITACHI_AC296 + + irsend.reset(); + irsend.sendRaw(rawData, 595, kHitachiAcFreq); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC296, irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc296Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Power: On, Mode: 7 (Auto), Temp: 1C, Fan: 5 (Auto)", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +// Decode a 'Synthetic' HitachiAc296 message. +TEST(TestDecodeHitachiAc296, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/pull/1758#issuecomment-1066017940 + // Power: ON, Temp: 24, Fan: Quiet (1), Mode: Cool + const uint8_t expected[kHitachiAc296StateLength] = { + 0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xCC, 0x33, 0x98, + 0x67, 0x42, 0xBD, 0x60, 0x9F, 0x00, 0xFF, 0x00, 0xFF, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x13, 0xEC, 0xF1, 0x0E, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x03, 0xFC}; + + irsend.reset(); + irsend.sendHitachiAc296(expected); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(HITACHI_AC296, irsend.capture.decode_type); + ASSERT_EQ(kHitachiAc296Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, kHitachiAc296Bits); + EXPECT_EQ( + "Power: On, Mode: 3 (Cool), Temp: 24C, Fan: 1 (Quiet)", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +TEST(TestIRHitachiAc296Class, SetAndGetPower) { + IRHitachiAc296 ac(kGpioUnused); + 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(TestIRHitachiAc296Class, SetAndGetTemp) { + IRHitachiAc296 ac(kGpioUnused); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + ac.setTemp(kHitachiAc296MinTemp); + EXPECT_EQ(kHitachiAc296MinTemp, ac.getTemp()); + ac.setTemp(kHitachiAc296MinTemp - 1); + EXPECT_EQ(kHitachiAc296MinTemp, ac.getTemp()); + ac.setTemp(kHitachiAc296MaxTemp); + EXPECT_EQ(kHitachiAc296MaxTemp, ac.getTemp()); + ac.setTemp(kHitachiAc296MaxTemp + 1); + EXPECT_EQ(kHitachiAc296MaxTemp, ac.getTemp()); + // Check the handling of the special temp value for auto operation mode, + ac.setMode(kHitachiAc296Cool); + ac.setTemp(kHitachiAc296TempAuto); + EXPECT_EQ(kHitachiAc296MinTemp, ac.getTemp()); + ac.setMode(kHitachiAc296Auto); + EXPECT_EQ(kHitachiAc296TempAuto, ac.getTemp()); + ac.setTemp(kHitachiAc296TempAuto); + EXPECT_EQ(kHitachiAc296TempAuto, ac.getTemp()); + ac.setTemp(kHitachiAc296MinTemp); + EXPECT_EQ(kHitachiAc296TempAuto, ac.getTemp()); + ac.setMode(kHitachiAc296Cool); + EXPECT_NE(kHitachiAc296TempAuto, ac.getTemp()); +} + +TEST(TestIRHitachiAc296Class, SetAndGetMode) { + IRHitachiAc296 ac(kGpioUnused); + ac.setMode(kHitachiAc296Cool); + ac.setFan(kHitachiAc296FanAuto); + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + EXPECT_EQ(kHitachiAc296Cool, ac.getMode()); + EXPECT_EQ(kHitachiAc296FanAuto, ac.getFan()); + ac.setMode(kHitachiAc296Heat); + EXPECT_EQ(25, ac.getTemp()); + EXPECT_EQ(kHitachiAc296Heat, ac.getMode()); + ac.setMode(kHitachiAc296Dehumidify); + EXPECT_EQ(kHitachiAc296Dehumidify, ac.getMode()); +} + +TEST(TestIRHitachiAc296Class, SetAndGetFan) { + IRHitachiAc296 ac(kGpioUnused); + ac.setMode(kHitachiAc296Cool); + ac.setFan(kHitachiAc296FanAuto); + EXPECT_EQ(kHitachiAc296FanAuto, ac.getFan()); + ac.setFan(kHitachiAc296FanLow); + EXPECT_EQ(kHitachiAc296FanLow, ac.getFan()); + ac.setFan(kHitachiAc296FanHigh); + EXPECT_EQ(kHitachiAc296FanHigh, ac.getFan()); + ac.setFan(kHitachiAc296FanAuto + 1); + EXPECT_EQ(kHitachiAc296FanAuto, ac.getFan()); + ac.setFan(kHitachiAc296FanSilent - 1); + EXPECT_EQ(kHitachiAc296FanSilent, ac.getFan()); +} diff --git a/lib/IRremoteESP8266/test/ir_Kelon_test.cpp b/lib/IRremoteESP8266/test/ir_Kelon_test.cpp index 7212d49ee..dec439a24 100644 --- a/lib/IRremoteESP8266/test/ir_Kelon_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Kelon_test.cpp @@ -105,6 +105,7 @@ TEST(TestDecodeKelon, Timer12HSmartMode) { EXPECT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(KELON, irsend.capture.decode_type); EXPECT_EQ(kKelonBits, irsend.capture.bits); + EXPECT_EQ(0x1679030683, irsend.capture.value); EXPECT_EQ( "Temp: 25C, Mode: 1 (Auto), Fan: 3 (High), Sleep: Off, Dry: 0, " "Timer: 12:00, Turbo: Off", @@ -124,6 +125,7 @@ TEST(TestDecodeKelon, Timer5_5hSuperCoolMode) { EXPECT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(KELON, irsend.capture.decode_type); EXPECT_EQ(kKelonBits, irsend.capture.bits); + EXPECT_EQ(0x100B0A010683, irsend.capture.value); EXPECT_EQ( "Temp: 18C, Mode: 2 (Cool), Fan: 1 (Low), Sleep: Off, Dry: 0, " "Timer: 05:30, Turbo: On", @@ -143,6 +145,7 @@ TEST(TestDecodeKelon, ChangeSettingsWithTimerSetHeatMode) { EXPECT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(KELON, irsend.capture.decode_type); EXPECT_EQ(kKelonBits, irsend.capture.bits); + EXPECT_EQ(0x58000683, irsend.capture.value); EXPECT_EQ( "Temp: 23C, Mode: 0 (Heat), Fan: 0 (Auto), Sleep: Off, Dry: 0, " "Timer: On, Turbo: Off", @@ -162,6 +165,7 @@ TEST(TestDecodeKelon, TestPowerToggleDryMode) { EXPECT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(KELON, irsend.capture.decode_type); EXPECT_EQ(kKelonBits, irsend.capture.bits); + EXPECT_EQ(0x83040683, irsend.capture.value); EXPECT_EQ( "Temp: 26C, Mode: 3 (Dry), Fan: 0 (Auto), Sleep: Off, Dry: 0, Timer:" " Off, Turbo: Off, Power Toggle: On", @@ -181,6 +185,7 @@ TEST(TestDecodeKelon, TestSwingToggleDryMode) { EXPECT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(KELON, irsend.capture.decode_type); EXPECT_EQ(kKelonBits, irsend.capture.bits); + EXPECT_EQ(0x83800683, irsend.capture.value); EXPECT_EQ( "Temp: 26C, Mode: 3 (Dry), Fan: 0 (Auto), Sleep: Off, Dry: 0, Timer:" " Off, Turbo: Off, Swing(V) Toggle: On", @@ -200,6 +205,7 @@ TEST(TestDecodeKelon, TestDryGradeNegativeValue) { EXPECT_TRUE(irrecv.decode(&irsend.capture)); EXPECT_EQ(KELON, irsend.capture.decode_type); EXPECT_EQ(kKelonBits, irsend.capture.bits); + EXPECT_EQ(0x83600683, irsend.capture.value); EXPECT_EQ( "Temp: 26C, Mode: 3 (Dry), Fan: 0 (Auto), Sleep: Off, Dry: -2," " Timer: Off, Turbo: Off", @@ -419,10 +425,177 @@ TEST(TestIRKelonClass, toCommonToggles) { } TEST(TestUtils, Housekeeping) { + // KELON ASSERT_EQ("KELON", typeToString(decode_type_t::KELON)); ASSERT_EQ(decode_type_t::KELON, strToDecodeType("KELON")); ASSERT_FALSE(hasACState(decode_type_t::KELON)); ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::KELON)); ASSERT_EQ(kKelonBits, IRsend::defaultBits(decode_type_t::KELON)); ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::KELON)); + + // KELON168 + ASSERT_EQ("KELON168", typeToString(decode_type_t::KELON168)); + ASSERT_EQ(decode_type_t::KELON168, strToDecodeType("KELON168")); + ASSERT_TRUE(hasACState(decode_type_t::KELON168)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::KELON168)); + ASSERT_EQ(kKelon168Bits, IRsend::defaultBits(decode_type_t::KELON168)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::KELON168)); +} + +TEST(TestDecodeKelon, Discussion1744) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/discussions/1744#discussioncomment-2061968 + // Note: This message is NOT at 48-bit Kelon protocol message. + // It is actually 48 + 64 + 56 = 168 bit (21 byte) message. + // i.e. A different protocol all together. + const uint16_t rawData[343] = { + 8922, 4494, // Header + 548, 1714, 524, 1718, 524, 572, 524, 550, // Byte 0 + 550, 556, 550, 560, 548, 586, 524, 1724, + 526, 564, 524, 1692, 550, 1696, 550, 552, // Byte 1 + 550, 554, 550, 560, 548, 586, 524, 550, + 548, 1688, 550, 1694, 548, 548, 550, 552, // Byte 2 + 548, 556, 548, 582, 524, 586, 524, 576, + 524, 540, 548, 568, 526, 572, 526, 548, // Byte 3 + 550, 1702, 550, 1706, 550, 1734, 526, 574, + 526, 564, 526, 544, 550, 548, 548, 550, // Byte 4 + 550, 554, 550, 582, 526, 586, 524, 550, + 548, 540, 550, 544, 550, 570, 526, 550, // Byte 5 + 550, 558, 546, 582, 524, 586, 524, 562, + 524, 7978, // Section Footer + 548, 1690, 548, 568, 524, 1696, 548, 1700, // Byte 6 + 548, 554, 550, 560, 548, 560, 550, 1722, + 526, 1686, 550, 1692, 548, 1696, 550, 576, // Byte 7 + 524, 1704, 548, 558, 550, 562, 548, 550, + 550, 564, 524, 570, 524, 548, 548, 576, // Byte 8 + 524, 578, 526, 558, 550, 586, 524, 574, + 524, 566, 524, 568, 526, 548, 548, 576, // Byte 9 + 526, 556, 548, 558, 550, 560, 550, 550, + 550, 564, 526, 544, 548, 548, 548, 552, // Byte 10 + 548, 556, 548, 582, 526, 564, 548, 550, + 550, 540, 548, 568, 526, 546, 550, 550, // Byte 11 + 550, 580, 524, 558, 550, 560, 550, 1698, + 550, 542, 548, 542, 550, 546, 550, 1722, // Byte 12 + 524, 1706, 548, 582, 526, 586, 524, 574, + 526, 1690, 548, 544, 550, 546, 550, 552, // Byte 13 + 548, 1726, 526, 1730, 524, 1734, 526, 562, + 524, 7976, // Section footer + 550, 566, 524, 544, 550, 570, 526, 550, // Byte 14 + 548, 554, 550, 1706, 550, 562, 548, 574, + 526, 542, 548, 544, 550, 572, 526, 548, // Byte 15 + 552, 554, 550, 582, 526, 584, 526, 574, + 524, 542, 548, 544, 548, 572, 524, 552, // Byte 16 + 548, 578, 524, 560, 548, 562, 548, 550, + 550, 540, 550, 546, 548, 572, 524, 576, // Byte 17 + 526, 556, 548, 560, 546, 564, 548, 574, + 526, 540, 550, 546, 546, 546, 550, 1698, // Byte 18 + 550, 1728, 524, 1706, 548, 564, 548, 574, + 524, 544, 548, 568, 526, 548, 548, 574, // Byte 19 + 524, 554, 550, 558, 550, 562, 548, 550, + 550, 566, 524, 544, 548, 546, 552, 1698, // Byte 20 + 548, 1702, 550, 560, 546, 586, 524, 538, + 546 // Footer + }; // KELON 178D000070030683 + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 343, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_NE(KELON, irsend.capture.decode_type); // Not a KELON message + EXPECT_NE(kKelonBits, irsend.capture.bits); // Not a 48 bit message. +} + +TEST(TestDecodeKelon168, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/discussions/1744#discussioncomment-2061968 + const uint16_t rawData[343] = { + 8922, 4494, // Header + 548, 1714, 524, 1718, 524, 572, 524, 550, // Byte 0 + 550, 556, 550, 560, 548, 586, 524, 1724, + 526, 564, 524, 1692, 550, 1696, 550, 552, // Byte 1 + 550, 554, 550, 560, 548, 586, 524, 550, + 548, 1688, 550, 1694, 548, 548, 550, 552, // Byte 2 + 548, 556, 548, 582, 524, 586, 524, 576, + 524, 540, 548, 568, 526, 572, 526, 548, // Byte 3 + 550, 1702, 550, 1706, 550, 1734, 526, 574, + 526, 564, 526, 544, 550, 548, 548, 550, // Byte 4 + 550, 554, 550, 582, 526, 586, 524, 550, + 548, 540, 550, 544, 550, 570, 526, 550, // Byte 5 + 550, 558, 546, 582, 524, 586, 524, 562, + 524, 7978, // Section Footer + 548, 1690, 548, 568, 524, 1696, 548, 1700, // Byte 6 + 548, 554, 550, 560, 548, 560, 550, 1722, + 526, 1686, 550, 1692, 548, 1696, 550, 576, // Byte 7 + 524, 1704, 548, 558, 550, 562, 548, 550, + 550, 564, 524, 570, 524, 548, 548, 576, // Byte 8 + 524, 578, 526, 558, 550, 586, 524, 574, + 524, 566, 524, 568, 526, 548, 548, 576, // Byte 9 + 526, 556, 548, 558, 550, 560, 550, 550, + 550, 564, 526, 544, 548, 548, 548, 552, // Byte 10 + 548, 556, 548, 582, 526, 564, 548, 550, + 550, 540, 548, 568, 526, 546, 550, 550, // Byte 11 + 550, 580, 524, 558, 550, 560, 550, 1698, + 550, 542, 548, 542, 550, 546, 550, 1722, // Byte 12 + 524, 1706, 548, 582, 526, 586, 524, 574, + 526, 1690, 548, 544, 550, 546, 550, 552, // Byte 13 + 548, 1726, 526, 1730, 524, 1734, 526, 562, + 524, 7976, // Section footer + 550, 566, 524, 544, 550, 570, 526, 550, // Byte 14 + 548, 554, 550, 1706, 550, 562, 548, 574, + 526, 542, 548, 544, 550, 572, 526, 548, // Byte 15 + 552, 554, 550, 582, 526, 584, 526, 574, + 524, 542, 548, 544, 548, 572, 524, 552, // Byte 16 + 548, 578, 524, 560, 548, 562, 548, 550, + 550, 540, 550, 546, 548, 572, 524, 576, // Byte 17 + 526, 556, 548, 560, 546, 564, 548, 574, + 526, 540, 550, 546, 546, 546, 550, 1698, // Byte 18 + 550, 1728, 524, 1706, 548, 564, 548, 574, + 524, 544, 548, 568, 526, 548, 548, 574, // Byte 19 + 524, 554, 550, 558, 550, 562, 548, 550, + 550, 566, 524, 544, 548, 546, 552, 1698, // Byte 20 + 548, 1702, 550, 560, 546, 586, 524, 538, + 546 // Footer + }; // KELON 178D000070030683 + + const uint8_t expected[kKelon168StateLength] = { + 0x83, 0x06, 0x03, 0x70, 0x00, 0x00, 0x8D, + 0x17, 0x00, 0x00, 0x00, 0x80, 0x18, 0x71, + 0x20, 0x00, 0x00, 0x00, 0x38, 0x00, 0x18}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 343, 38000); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::KELON168, irsend.capture.decode_type); + EXPECT_EQ(kKelon168Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ("", IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +TEST(TestDecodeKelon168, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + const uint8_t expected[kKelon168StateLength] = { + 0x83, 0x06, 0x03, 0x70, 0x00, 0x00, 0x8D, + 0x17, 0x00, 0x00, 0x00, 0x80, 0x18, 0x71, + 0x20, 0x00, 0x00, 0x00, 0x38, 0x00, 0x18}; + + irsend.begin(); + irsend.reset(); + irsend.sendKelon168(expected); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::KELON168, irsend.capture.decode_type); + EXPECT_EQ(kKelon168Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ("", IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } diff --git a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp index 73ad6581d..e4a335db0 100644 --- a/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Kelvinator_test.cpp @@ -250,24 +250,44 @@ TEST(TestKelvinatorClass, VaneSwing) { IRKelvinatorAC irkelv(0); irkelv.begin(); - irkelv.setSwingHorizontal(true); - irkelv.setSwingVertical(false); - irkelv.setSwingHorizontal(true); EXPECT_TRUE(irkelv.getSwingHorizontal()); - EXPECT_FALSE(irkelv.getSwingVertical()); - irkelv.setSwingVertical(true); + EXPECT_FALSE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVOff, irkelv.getSwingVerticalPosition()); + + irkelv.setSwingVertical(true, kKelvinatorSwingVAuto); + EXPECT_TRUE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVAuto, irkelv.getSwingVerticalPosition()); + EXPECT_TRUE(irkelv.getSwingHorizontal()); + + irkelv.setSwingVertical(false, kKelvinatorSwingVMiddle); + EXPECT_FALSE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVMiddle, irkelv.getSwingVerticalPosition()); EXPECT_TRUE(irkelv.getSwingHorizontal()); - EXPECT_TRUE(irkelv.getSwingVertical()); irkelv.setSwingHorizontal(false); EXPECT_FALSE(irkelv.getSwingHorizontal()); - EXPECT_TRUE(irkelv.getSwingVertical()); - irkelv.setSwingVertical(false); + irkelv.setSwingVertical(true, kKelvinatorSwingVLowAuto); + EXPECT_TRUE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVLowAuto, irkelv.getSwingVerticalPosition()); EXPECT_FALSE(irkelv.getSwingHorizontal()); - EXPECT_FALSE(irkelv.getSwingVertical()); + + // Out of bounds. + irkelv.setSwingVertical(false, 255); + EXPECT_FALSE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVOff, irkelv.getSwingVerticalPosition()); + irkelv.setSwingVertical(false, kKelvinatorSwingVAuto); + EXPECT_FALSE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVOff, irkelv.getSwingVerticalPosition()); + + irkelv.setSwingVertical(true, 255); + EXPECT_TRUE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVAuto, irkelv.getSwingVerticalPosition()); + irkelv.setSwingVertical(true, kKelvinatorSwingVLowest); + EXPECT_TRUE(irkelv.getSwingVerticalAuto()); + EXPECT_EQ(kKelvinatorSwingVAuto, irkelv.getSwingVerticalPosition()); } TEST(TestKelvinatorClass, QuietMode) { @@ -425,7 +445,7 @@ TEST(TestKelvinatorClass, HumanReadable) { EXPECT_EQ( "Power: Off, Mode: 0 (Auto), Temp: 16C, Fan: 0 (Auto), Turbo: Off, " "Quiet: Off, XFan: Off, Ion: Off, Light: Off, " - "Swing(H): Off, Swing(V): Off", + "Swing(H): Off, Swing(V): 0 (Off)", irkelv.toString()); irkelv.on(); irkelv.setMode(kKelvinatorCool); @@ -438,7 +458,7 @@ TEST(TestKelvinatorClass, HumanReadable) { EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 25C, Fan: 5 (High), Turbo: Off, " "Quiet: Off, XFan: On, Ion: On, Light: On, " - "Swing(H): On, Swing(V): Off", + "Swing(H): On, Swing(V): 0 (Off)", irkelv.toString()); } @@ -451,7 +471,7 @@ TEST(TestKelvinatorClass, MessageConstuction) { irkelv.setFan(1); irkelv.setMode(kKelvinatorCool); irkelv.setTemp(27); - irkelv.setSwingVertical(false); + irkelv.setSwingVertical(false, kKelvinatorSwingVOff); irkelv.setSwingHorizontal(true); irkelv.setIonFilter(true); irkelv.setQuiet(false); @@ -464,7 +484,7 @@ TEST(TestKelvinatorClass, MessageConstuction) { EXPECT_EQ(1, irkelv.getFan()); EXPECT_EQ(kKelvinatorCool, irkelv.getMode()); EXPECT_EQ(27, irkelv.getTemp()); - EXPECT_FALSE(irkelv.getSwingVertical()); + EXPECT_FALSE(irkelv.getSwingVerticalAuto()); EXPECT_TRUE(irkelv.getSwingHorizontal()); EXPECT_TRUE(irkelv.getIonFilter()); EXPECT_FALSE(irkelv.getQuiet()); @@ -523,7 +543,7 @@ TEST(TestDecodeKelvinator, NormalSynthetic) { EXPECT_EQ( "Power: On, Mode: 1 (Cool), Temp: 27C, Fan: 1 (Low), Turbo: Off, " "Quiet: Off, XFan: On, Ion: Off, Light: Off, " - "Swing(H): Off, Swing(V): Off", + "Swing(H): Off, Swing(V): 0 (Off)", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -541,7 +561,7 @@ TEST(TestKelvinatorClass, toCommon) { ac.setTurbo(true); ac.setLight(true); ac.setSwingHorizontal(false); - ac.setSwingVertical(true); + ac.setSwingVertical(true, kKelvinatorSwingVAuto); // Now test it. ASSERT_EQ(decode_type_t::KELVINATOR, ac.toCommon().protocol); diff --git a/lib/IRremoteESP8266/test/ir_LG_test.cpp b/lib/IRremoteESP8266/test/ir_LG_test.cpp index 808f7b83a..9652f702c 100644 --- a/lib/IRremoteESP8266/test/ir_LG_test.cpp +++ b/lib/IRremoteESP8266/test/ir_LG_test.cpp @@ -693,6 +693,10 @@ TEST(TestUtils, Housekeeping) { IRac::strToModel(irutils::modelToStr( decode_type_t::LG2, lg_ac_remote_model_t::AKB73757604).c_str())); + ASSERT_EQ(lg_ac_remote_model_t::LG6711A20083V, + IRac::strToModel(irutils::modelToStr( + decode_type_t::LG, + lg_ac_remote_model_t::LG6711A20083V).c_str())); } TEST(TestIRLgAcClass, KnownExamples) { @@ -1126,3 +1130,63 @@ TEST(TestIRLgAcClass, SwingVOffAfterAuto) { ASSERT_EQ("Model: 3 (AKB74955603), Swing(V): 21 (Off)", IRAcUtils::resultAcToString(&ac._irsend.capture)); } + +TEST(TestIRLgAcClass, Issue1737) { + // Simulate getting a light toggle while the ac class/prev state thinks the + // device is on. + IRLgAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(lg_ac_remote_model_t::AKB74955603); + ac.setPower(true); + ac.setMode(kLgAcFan); + ac.setTemp(18); + ac.setFan(kLgAcFanLowest); + stdAc::state_t prev = ac.toCommon(); + EXPECT_TRUE(prev.power); + EXPECT_TRUE(ac.toCommon().power); + EXPECT_TRUE(ac.toCommon(&prev).power); + + ac.setRaw(kLgAcLightToggle); + EXPECT_TRUE(ac.isLightToggle()); + + EXPECT_FALSE(ac.toCommon().power); // No previous state, so should be false. + EXPECT_TRUE(ac.toCommon(&prev).power); + + // A better simulation of the desired use case. + IRsendTest irsend(kGpioUnused); + IRrecv capture(kGpioUnused); + irsend.begin(); + irsend.reset(); + irsend.sendLG2(kLgAcLightToggle); + irsend.makeDecodeResult(); + EXPECT_TRUE(capture.decode(&irsend.capture)); + ASSERT_EQ(LG2, irsend.capture.decode_type); // Not "LG" + ASSERT_EQ(kLgBits, irsend.capture.bits); + stdAc::state_t result; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev)); + EXPECT_TRUE(result.power); +} + +TEST(TestIRLgAcClass, SwingVToggle) { + IRLgAc ac(kGpioUnused); + ac.begin(); + + ac.setModel(lg_ac_remote_model_t::GE6711AR2853M); + EXPECT_FALSE(ac.isSwingVToggle()); + EXPECT_FALSE(ac._isLG6711A20083V()); + EXPECT_EQ(lg_ac_remote_model_t::GE6711AR2853M, ac.getModel()); + + ac.setRaw(kLgAcSwingVToggle, decode_type_t::LG); + EXPECT_TRUE(ac.isSwingVToggle()); + EXPECT_TRUE(ac._isLG6711A20083V()); + EXPECT_EQ(lg_ac_remote_model_t::LG6711A20083V, ac.getModel()); + + EXPECT_EQ("Model: 5 (LG6711A20083V), Swing(V): Toggle", ac.toString()); + + ac.stateReset(); + ac.setModel(lg_ac_remote_model_t::LG6711A20083V); + ac.setSwingV(kLgAcSwingVToggle); + EXPECT_EQ(ac._swingv, kLgAcSwingVToggle); + EXPECT_NE(ac._swingv_prev, kLgAcSwingVToggle); +} diff --git a/lib/IRremoteESP8266/test/ir_Midea_test.cpp b/lib/IRremoteESP8266/test/ir_Midea_test.cpp index 713fcd0e4..0660f8afc 100644 --- a/lib/IRremoteESP8266/test/ir_Midea_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Midea_test.cpp @@ -448,8 +448,8 @@ TEST(TestMideaACClass, HumanReadableOutput) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.off(); ac.setTemp(25, true); ac.setFan(kMideaACFanHigh); @@ -458,21 +458,21 @@ TEST(TestMideaACClass, HumanReadableOutput) { EXPECT_EQ( "Type: 1 (Command), Power: Off, Mode: 1 (Dry), Celsius: Off, " "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 3 (High), " - "Sleep: On, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: On, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setUseCelsius(true); EXPECT_EQ( "Type: 1 (Command), Power: Off, Mode: 1 (Dry), Celsius: On, " "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 3 (High), " - "Sleep: On, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: On, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setRaw(0xA19867FFFF7E); EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 0 (Cool), Celsius: Off, " "Temp: 21C/69F, On Timer: Off, Off Timer: Off, Fan: 3 (High), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); } // Tests for decodeMidea(). @@ -681,8 +681,8 @@ TEST(TestDecodeMidea, DecodeRealExample) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " "Temp: 18C/65F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", IRAcUtils::resultAcToString(&irsend.capture)); stdAc::state_t r, p; ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); @@ -733,16 +733,16 @@ TEST(TestMideaACClass, CelsiusRemoteTemp) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 0 (Cool), Celsius: On, " "Temp: 17C/62F, On Timer: Off, Off Timer: Off, Fan: 1 (Low), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setRaw(on_cool_low_17c); EXPECT_EQ(17, ac.getTemp(true)); EXPECT_EQ(62, ac.getTemp(false)); EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 0 (Cool), Celsius: On, " "Temp: 17C/62F, On Timer: Off, Off Timer: Off, Fan: 1 (Low), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setTemp(17, true); EXPECT_EQ(17, ac.getTemp(true)); EXPECT_EQ(62, ac.getTemp(false)); @@ -752,8 +752,8 @@ TEST(TestMideaACClass, CelsiusRemoteTemp) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 0 (Cool), Celsius: On, " "Temp: 30C/86F, On Timer: Off, Off Timer: Off, Fan: 1 (Low), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); } // https://github.com/crankyoldgit/IRremoteESP8266/issues/819 @@ -766,18 +766,18 @@ TEST(TestMideaACClass, SwingV) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: On, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): Toggle, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setSwingVToggle(false); ASSERT_FALSE(ac.getSwingVToggle()); EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setRaw(kMideaACToggleSwingV); - EXPECT_EQ("Type: 2 (Special), Swing(V) Toggle: On, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", + EXPECT_EQ("Type: 2 (Special), Swing(V): Toggle, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); } @@ -791,18 +791,18 @@ TEST(TestMideaACClass, Econo) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: On, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: Toggle, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setEconoToggle(false); ASSERT_FALSE(ac.getEconoToggle()); EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setRaw(kMideaACToggleEcono); - EXPECT_EQ("Type: 2 (Special), Swing(V) Toggle: Off, Econo Toggle: On, " - "Turbo Toggle: Off, Light Toggle: Off", + EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: Toggle, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); } @@ -815,18 +815,20 @@ TEST(TestMideaACClass, Turbo) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: On, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: Toggle, Quiet: Off, Light: -, Clean: -, 8C Heat: -", + ac.toString()); ac.setTurboToggle(false); ASSERT_FALSE(ac.getTurboToggle()); EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", + ac.toString()); ac.setRaw(kMideaACToggleTurbo); - EXPECT_EQ("Type: 2 (Special), Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: On, Light Toggle: Off", + EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: -, " + "Turbo: Toggle, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); } @@ -839,18 +841,105 @@ TEST(TestMideaACClass, Light) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: On", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: Toggle, Clean: -, 8C Heat: -", + ac.toString()); ac.setLightToggle(false); ASSERT_FALSE(ac.getLightToggle()); EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", + ac.toString()); ac.setRaw(kMideaACToggleLight); - EXPECT_EQ("Type: 2 (Special), Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: On", + EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: Toggle, Clean: -, 8C Heat: -", + ac.toString()); +} + +TEST(TestMideaACClass, Clean) { + IRMideaAC ac(kGpioUnused); + ac.setCleanToggle(false); + ASSERT_FALSE(ac.getCleanToggle()); + ac.setCleanToggle(true); + ASSERT_TRUE(ac.getCleanToggle()); + EXPECT_EQ( + "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " + "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: Toggle, 8C Heat: -", + ac.toString()); + ac.setCleanToggle(false); + ASSERT_FALSE(ac.getCleanToggle()); + EXPECT_EQ( + "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " + "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", + ac.toString()); + ac.setRaw(kMideaACToggleSelfClean); + EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: Toggle, 8C Heat: -", + ac.toString()); +} + +TEST(TestMideaACClass, FreezeProtectionAKA8CHeat) { + IRMideaAC ac(kGpioUnused); + ac.set8CHeatToggle(false); + ASSERT_FALSE(ac.get8CHeatToggle()); + ASSERT_NE(kMideaACHeat, ac.getMode()); + ac.set8CHeatToggle(true); // Not in heat mode so it should not work. + ASSERT_FALSE(ac.get8CHeatToggle()); + ac.setMode(kMideaACHeat); // Now it should work + ac.set8CHeatToggle(true); + ASSERT_TRUE(ac.get8CHeatToggle()); + EXPECT_EQ( + "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: Off, " + "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: Toggle", + ac.toString()); + ac.set8CHeatToggle(false); + ASSERT_FALSE(ac.get8CHeatToggle()); + EXPECT_EQ( + "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: Off, " + "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", + ac.toString()); + ac.setRaw(kMideaACToggle8CHeat); + EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: Toggle", + ac.toString()); +} + +TEST(TestMideaACClass, Quiet) { + IRMideaAC ac(kGpioUnused); + ac.setQuiet(false); + ASSERT_FALSE(ac.getQuiet()); + ac.setQuiet(true); + ASSERT_TRUE(ac.getQuiet()); + EXPECT_EQ( + "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " + "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: On, Light: -, Clean: -, 8C Heat: -", + ac.toString()); + ac.setQuiet(false); + ASSERT_FALSE(ac.getQuiet()); + EXPECT_EQ( + "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " + "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); + ac.setRaw(kMideaACQuietOn); + EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: -, " + "Turbo: -, Quiet: On, Light: -, Clean: -, 8C Heat: -", + ac.toString()); + ac.setRaw(kMideaACQuietOff); + EXPECT_EQ("Type: 2 (Special), Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); } @@ -1067,8 +1156,8 @@ TEST(TestDecodeMidea, Issue1318_self_decode) { EXPECT_EQ( "Type: 4 (Follow), Power: On, Mode: 3 (Heat), Celsius: On, " "Temp: 24C/75F, SensorTemp: 24C/75F, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", IRAcUtils::resultAcToString(&irsend.capture)); EXPECT_EQ( "f38000d50" @@ -1146,32 +1235,32 @@ TEST(TestMideaACClass, SensorTemp) { EXPECT_EQ( "Type: 4 (Follow), Power: On, Mode: 2 (Auto), Celsius: On, " "Temp: 23C/73F, SensorTemp: 0C/32F, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setRaw(0xA482467F134E); // 18C EXPECT_EQ(18, ac.getSensorTemp(true)); EXPECT_TRUE(ac.getEnableSensorTemp()); EXPECT_EQ( "Type: 4 (Follow), Power: On, Mode: 2 (Auto), Celsius: On, " "Temp: 23C/73F, SensorTemp: 18C/64F, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setRaw(0xA482467F266B); // 37C EXPECT_EQ(37, ac.getSensorTemp(true)); EXPECT_TRUE(ac.getEnableSensorTemp()); EXPECT_EQ( "Type: 4 (Follow), Power: On, Mode: 2 (Auto), Celsius: On, " "Temp: 23C/73F, SensorTemp: 37C/98F, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setRaw(0xA482607F2B43); // 74F EXPECT_EQ(74, ac.getSensorTemp(false)); EXPECT_TRUE(ac.getEnableSensorTemp()); EXPECT_EQ( "Type: 4 (Follow), Power: On, Mode: 2 (Auto), Celsius: Off, " "Temp: 17C/62F, SensorTemp: 23C/74F, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); } TEST(TestMideaACClass, MessageType) { @@ -1202,8 +1291,8 @@ TEST(TestMideaACClass, ConstructKnownMessage) { EXPECT_EQ( "Type: 4 (Follow), Power: On, Mode: 2 (Auto), Celsius: On, " "Temp: 23C/73F, SensorTemp: 25C/77F, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); EXPECT_EQ(followme_25C, ac.getRaw()); } @@ -1217,8 +1306,8 @@ TEST(TestMideaACClass, OnTimer) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: On, " "Temp: 30C/86F, On Timer: 07:00, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setOnTimer(0); // Disable / Timer off. EXPECT_FALSE(ac.isOnTimerEnabled()); EXPECT_EQ(kMideaACTypeCommand, ac.getType()); @@ -1229,8 +1318,8 @@ TEST(TestMideaACClass, OnTimer) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: On, " "Temp: 30C/86F, On Timer: 03:30, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); // Limit to max time. ac.setOnTimer(25 * 60); // 25h EXPECT_TRUE(ac.isOnTimerEnabled()); @@ -1239,8 +1328,8 @@ TEST(TestMideaACClass, OnTimer) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: On, " "Temp: 30C/86F, On Timer: 24:00, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); } @@ -1254,8 +1343,8 @@ TEST(TestMideaACClass, OffTimer) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: On, " "Temp: 30C/86F, On Timer: Off, Off Timer: 00:30, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.setOffTimer(0); // Disable / Timer off. EXPECT_FALSE(ac.isOffTimerEnabled()); EXPECT_EQ(kMideaACTypeCommand, ac.getType()); @@ -1266,8 +1355,8 @@ TEST(TestMideaACClass, OffTimer) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: On, " "Temp: 30C/86F, On Timer: Off, Off Timer: 03:30, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); // Limit to max time. ac.setOffTimer(25 * 60); // 25h EXPECT_TRUE(ac.isOffTimerEnabled()); @@ -1276,8 +1365,8 @@ TEST(TestMideaACClass, OffTimer) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: On, " "Temp: 30C/86F, On Timer: Off, Off Timer: 24:00, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", ac.toString()); + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", ac.toString()); } // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1318#issuecomment-725406813 @@ -1294,16 +1383,16 @@ TEST(TestMideaACClass, SendingSwingV) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 2 (Auto), Celsius: Off, " "Temp: 25C/77F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), " - "Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", + "Sleep: Off, Swing(V): -, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", IRAcUtils::resultAcToString(&ac._irsend.capture)); // Decode the second/following message which should be the swing toggle. EXPECT_TRUE(irrecv.decodeMidea(&ac._irsend.capture, 201)); EXPECT_EQ(MIDEA, ac._irsend.capture.decode_type); EXPECT_EQ(kMideaBits, ac._irsend.capture.bits); EXPECT_EQ( - "Type: 2 (Special), Swing(V) Toggle: On, Econo Toggle: Off, " - "Turbo Toggle: Off, Light Toggle: Off", + "Type: 2 (Special), Swing(V): Toggle, Econo: -, " + "Turbo: -, Quiet: Off, Light: -, Clean: -, 8C Heat: -", IRAcUtils::resultAcToString(&ac._irsend.capture)); } @@ -1318,8 +1407,8 @@ TEST(TestMideaACClass, Issue1342) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: Off, " "Temp: 21C/70F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), Sleep: Off, " - "Swing(V) Toggle: Off, Econo Toggle: Off, Turbo Toggle: Off, " - "Light Toggle: Off", + "Swing(V): -, Econo: -, Turbo: -, Quiet: Off, " + "Light: -, Clean: -, 8C Heat: -", ac.toString()); ac.stateReset(); // Remove any existing info so we can build from scratch. ASSERT_NE(0xA1A368FFFF45, ac.getRaw()); @@ -1337,8 +1426,8 @@ TEST(TestMideaACClass, Issue1342) { EXPECT_EQ( "Type: 1 (Command), Power: On, Mode: 3 (Heat), Celsius: Off, " "Temp: 21C/70F, On Timer: Off, Off Timer: Off, Fan: 0 (Auto), Sleep: Off, " - "Swing(V) Toggle: Off, Econo Toggle: Off, Turbo Toggle: Off, " - "Light Toggle: Off", + "Swing(V): -, Econo: -, Turbo: -, Quiet: Off, " + "Light: -, Clean: -, 8C Heat: -", ac.toString()); EXPECT_EQ(0xA1A368FFFF45, ac.getRaw()); } diff --git a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp index 815d4f996..cbed3084e 100644 --- a/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Mitsubishi_test.cpp @@ -353,7 +353,7 @@ TEST(TestSendMitsubishiAC, SendDataOnly) { "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s1300m450s1300m450s1300m450s1300m450s1300m450s420m450s420m450s420" - "m440s17100" + "m440s15500" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -373,7 +373,7 @@ TEST(TestSendMitsubishiAC, SendDataOnly) { "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s1300m450s1300m450s1300m450s1300m450s1300m450s420m450s420m450s420" - "m440s17100", + "m440s15500", irsend.outputStr()); } @@ -409,7 +409,7 @@ TEST(TestSendMitsubishiAC, SendWithRepeats) { "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s1300m450s1300m450s1300m450s1300m450s1300m450s420m450s420m450s420" - "m440s17100", + "m440s15500", irsend.outputStr()); irsend.reset(); @@ -435,7 +435,7 @@ TEST(TestSendMitsubishiAC, SendWithRepeats) { "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s1300m450s1300m450s1300m450s1300m450s1300m450s420m450s420m450s420" - "m440s17100" + "m440s15500" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -455,7 +455,7 @@ TEST(TestSendMitsubishiAC, SendWithRepeats) { "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s1300m450s1300m450s1300m450s1300m450s1300m450s420m450s420m450s420" - "m440s17100" + "m440s15500" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -475,7 +475,7 @@ TEST(TestSendMitsubishiAC, SendWithRepeats) { "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s1300m450s1300m450s1300m450s1300m450s1300m450s420m450s420m450s420" - "m440s17100", + "m440s15500", irsend.outputStr()); } @@ -518,7 +518,7 @@ TEST(TestSendMitsubishiAC, SendUnexpectedSizes) { "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s1300m450s1300m450s1300m450s1300m450s1300m450s420m450s420m450s420" "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" - "m440s17100" + "m440s15500" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -539,7 +539,7 @@ TEST(TestSendMitsubishiAC, SendUnexpectedSizes) { "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s1300m450s1300m450s1300m450s1300m450s1300m450s420m450s420m450s420" "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" - "m440s17100", + "m440s15500", irsend.outputStr()); } @@ -751,7 +751,7 @@ TEST(TestMitsubishiACClass, MessageConstuction) { "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s420m450s420m450s420m450s420m450s1300m450s420m450s420m450s420" "m450s420m450s1300m450s1300m450s1300m450s1300m450s420m450s1300m450s420" - "m440s17100" + "m440s15500" "m3400s1750" "m450s1300m450s1300m450s420m450s420m450s420m450s1300m450s420m450s420" "m450s1300m450s1300m450s420m450s1300m450s420m450s420m450s1300m450s1300" @@ -771,7 +771,7 @@ TEST(TestMitsubishiACClass, MessageConstuction) { "m450s420m450s420m450s420m450s420m450s420m450s420m450s420m450s420" "m450s420m450s420m450s420m450s420m450s1300m450s420m450s420m450s420" "m450s420m450s1300m450s1300m450s1300m450s1300m450s420m450s1300m450s420" - "m440s17100", + "m440s15500", irsend.outputStr()); } @@ -1712,3 +1712,74 @@ TEST(TestMitsubishiACClass, WeeklyTimerEnabled) { ac.setRaw(weekly_off); EXPECT_FALSE(ac.getWeeklyTimerEnabled()); } + +// Decode a 'real' example for a MLZ-RX5017AS A/C +TEST(TestDecodeMitsubishiAC, Issue1759) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + const uint16_t rawData[583] = { + 3392, 1638, 456, 1220, 456, 1220, 456, 382, 454, 382, 456, 380, 456, 1222, + 454, 380, 456, 382, 456, 1220, 456, 1220, 456, 382, 454, 1222, 454, 382, + 456, 384, 454, 1222, 454, 1220, 454, 382, 456, 1220, 456, 1220, 456, 382, + 456, 382, 456, 1220, 456, 380, 456, 382, 456, 1220, 456, 380, 456, 382, + 456, 382, 456, 380, 456, 382, 456, 380, 458, 382, 456, 380, 456, 382, 456, + 382, 456, 382, 456, 382, 456, 382, 456, 382, 456, 382, 456, 382, 456, 382, + 456, 380, 458, 382, 456, 380, 456, 1220, 456, 380, 456, 382, 456, 382, + 456, 382, 456, 382, 456, 1220, 456, 382, 456, 382, 456, 382, 456, 380, + 456, 382, 456, 382, 456, 1220, 456, 382, 456, 382, 456, 382, 456, 382, + 454, 382, 456, 382, 456, 382, 456, 382, 454, 384, 454, 382, 456, 380, 456, + 382, 456, 382, 456, 382, 456, 382, 454, 382, 456, 382, 456, 382, 456, 382, + 456, 1222, 454, 1220, 456, 382, 456, 382, 456, 382, 454, 1220, 456, 1220, + 454, 1222, 454, 1220, 454, 382, 456, 382, 456, 382, 454, 382, 454, 382, + 456, 382, 456, 382, 456, 382, 454, 382, 456, 380, 456, 382, 454, 382, 454, + 384, 454, 382, 456, 382, 454, 384, 454, 382, 456, 382, 456, 382, 456, 384, + 454, 384, 454, 382, 456, 382, 454, 382, 454, 384, 454, 382, 456, 1220, + 456, 382, 456, 382, 454, 382, 456, 382, 456, 382, 454, 1220, 456, 382, + 456, 382, 456, 382, 456, 382, 454, 382, 456, 382, 456, 382, 454, 382, 456, + 382, 454, 382, 456, 382, 456, 382, 456, 382, 456, 382, 456, 382, 456, 382, + 454, 1220, 456, 1220, 456, 380, 456, 1220, 456, 1220, 456, 1222, 454, + 1220, 456, 1220, 456, 11280, + 3392, 1638, 454, 1220, 456, 1220, 456, 382, 456, 382, 456, 382, 456, 1220, + 456, 380, 456, 382, 456, 1220, 456, 1220, 454, 382, 456, 1220, 456, 382, + 456, 382, 456, 1220, 456, 1222, 454, 382, 456, 1220, 456, 1220, 454, 382, + 456, 382, 456, 1220, 454, 382, 456, 382, 456, 1220, 456, 382, 456, 382, + 454, 382, 456, 382, 454, 382, 456, 382, 456, 382, 456, 382, 456, 382, 454, + 382, 456, 382, 456, 382, 456, 382, 456, 382, 456, 382, 456, 382, 454, 382, + 456, 382, 456, 382, 456, 382, 456, 1220, 456, 382, 456, 382, 456, 382, + 454, 382, 456, 382, 456, 1220, 456, 380, 456, 382, 456, 382, 456, 382, + 456, 382, 456, 382, 456, 1220, 456, 382, 454, 382, 456, 382, 454, 382, + 456, 382, 456, 382, 456, 382, 456, 382, 456, 382, 456, 380, 456, 382, 456, + 380, 456, 382, 456, 382, 456, 382, 454, 382, 456, 382, 454, 382, 456, 382, + 456, 1220, 454, 1220, 456, 382, 456, 382, 456, 382, 454, 1220, 456, 1220, + 454, 1222, 454, 1220, 454, 382, 456, 382, 456, 382, 456, 382, 456, 382, + 456, 382, 456, 382, 454, 382, 456, 382, 456, 382, 456, 382, 456, 382, 456, + 382, 456, 382, 456, 382, 454, 382, 456, 382, 456, 382, 456, 382, 456, 382, + 456, 382, 456, 382, 456, 380, 456, 382, 456, 382, 456, 382, 456, 1220, + 456, 382, 456, 382, 456, 382, 454, 382, 456, 382, 456, 1222, 454, 380, + 456, 382, 456, 382, 456, 382, 456, 380, 456, 382, 456, 382, 454, 382, 454, + 382, 456, 382, 456, 382, 456, 382, 456, 382, 456, 382, 456, 382, 456, 382, + 456, 1220, 456, 1220, 454, 382, 456, 1222, 454, 1220, 454, 1220, 454, + 1222, 454, 1220, 456}; // UNKNOWN 76CCC723 + irsend.sendRaw(rawData, 583, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI_AC, irsend.capture.decode_type); + EXPECT_EQ(kMitsubishiACBits, irsend.capture.bits); + uint8_t expected[kMitsubishiACStateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x04, 0x00, 0xC0, + 0x78, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0xFB}; + EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "Power: On, Mode: 1 (Heat), Temp: 20C, Fan: 0 (Auto), " + "Swing(V): 0 (Auto), Swing(H): 0 (UNKNOWN), Clock: 20:00, " + "On Timer: 00:00, Off Timer: 00:00, Timer: -, Weekly Timer: Off, " + "10C Heat: Off, ISee: Off, Econo: Off, Absense detect: Off, " + "Direct / Indirect Mode: 0, Fresh: Off", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} diff --git a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp index 6378e7679..bcfaef972 100644 --- a/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Sanyo_test.cpp @@ -284,6 +284,14 @@ TEST(TestUtils, Housekeeping) { ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::SANYO_AC88)); ASSERT_EQ(kSanyoAc88Bits, IRsend::defaultBits(decode_type_t::SANYO_AC88)); ASSERT_EQ(kSanyoAc88MinRepeat, IRsend::minRepeats(decode_type_t::SANYO_AC88)); + // Sanyo A/C 152 Bit. + ASSERT_EQ("SANYO_AC152", typeToString(decode_type_t::SANYO_AC152)); + ASSERT_EQ(decode_type_t::SANYO_AC152, strToDecodeType("SANYO_AC152")); + ASSERT_TRUE(hasACState(decode_type_t::SANYO_AC152)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::SANYO_AC152)); + ASSERT_EQ(kSanyoAc152Bits, IRsend::defaultBits(decode_type_t::SANYO_AC152)); + ASSERT_EQ(kSanyoAc152MinRepeat, + IRsend::minRepeats(decode_type_t::SANYO_AC152)); } TEST(TestDecodeSanyoAc, DecodeRealExamples) { @@ -820,3 +828,70 @@ TEST(TestSanyoAc88Class, Clock) { ac.setClock(25 * 60 + 61); EXPECT_EQ(23 * 60 + 59, ac.getClock()); } + +TEST(TestDecodeSanyoAc152, DecodeRealExamples) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + // Ref: "16c" from https://github.com/crankyoldgit/IRremoteESP8266/issues/1826#issuecomment-1160708653 + const uint16_t rawData[307] = { + 3294, 1726, 420, 330, 458, 462, 382, 452, 438, 330, 456, 458, 384, 454, + 384, 1312, 422, 324, 512, 336, 454, 458, 384, 450, 438, 358, 436, 464, + 424, 326, 458, 476, 372, 458, 430, 328, 458, 464, 382, 1308, 424, 326, + 510, 1264, 424, 268, 520, 460, 380, 460, 436, 324, 462, 400, 436, 474, + 372, 456, 430, 342, 452, 450, 388, 446, 442, 1262, 422, 266, 520, 1314, + 372, 1306, 424, 1258, 370, 390, 448, 1314, 372, 1310, 426, 308, 522, 338, + 454, 470, 370, 454, 438, 330, 456, 468, 370, 456, 384, 464, 384, 1306, + 422, 328, 460, 472, 374, 448, 442, 1258, 426, 1256, 426, 268, 520, 464, + 382, 460, 430, 328, 508, 1264, 426, 262, 572, 1262, 424, 228, 604, 1262, + 372, 1312, 372, 1310, 426, 1256, 422, 1258, 424, 262, 524, 418, 428, 456, + 382, 1308, 372, 456, 386, 456, 382, 464, 378, 1308, 424, 360, 436, 454, + 430, 344, 450, 1306, 372, 1310, 424, 326, 510, 338, 452, 456, 384, 456, + 436, 328, 510, 1258, 372, 1310, 422, 338, 454, 466, 424, 328, 460, 1310, + 372, 1312, 424, 1258, 450, 262, 496, 1310, 372, 1310, 426, 1260, 424, 260, + 526, 442, 442, 1264, 426, 268, 520, 458, 380, 450, 386, 462, 436, 320, + 518, 1256, 372, 394, 446, 398, 494, 334, 506, 326, 510, 276, 518, 460, + 430, 332, 508, 326, 510, 356, 440, 448, 444, 264, 572, 336, 456, 408, 434, + 454, 438, 332, 506, 336, 454, 460, 384, 448, 444, 326, 510, 332, 456, 442, + 400, 456, 384, 456, 386, 452, 388, 454, 440, 326, 512, 272, 518, 450, 440, + 334, 454, 458, 440, 320, 516, 268, 570, 340, 452, 468, 424, 260, 574, 336, + 456, 394, 444, 458, 438, 328, 508, 1258, 370, 1312, 372, 1312, 424, 314, + 468, 1312, 450, 1232, 370, 1314, 420, 324, 514}; // UNKNOWN 584FBE80 + + const uint8_t expectedState[kSanyoAc152StateLength] = { + 0x40, 0x00, 0x14, 0x80, 0x6E, 0x80, 0x18, 0xEA, 0x23, 0x62, + 0x30, 0xEE, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x77}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 307, 38000); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SANYO_AC152, irsend.capture.decode_type); + EXPECT_EQ(kSanyoAc152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); +} + +TEST(TestDecodeSanyoAc152, SyntheticSelfDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + const uint8_t expectedState[kSanyoAc152StateLength] = { + 0x40, 0x00, 0x14, 0x80, 0x6E, 0x80, 0x18, 0xEA, 0x23, 0x62, + 0x30, 0xEE, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x77}; + irsend.begin(); + irsend.reset(); + irsend.sendSanyoAc152(expectedState); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SANYO_AC152, irsend.capture.decode_type); + EXPECT_EQ(kSanyoAc152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); +} diff --git a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp index b7ab5301f..f1a4cf393 100644 --- a/lib/IRremoteESP8266/test/ir_Tcl_test.cpp +++ b/lib/IRremoteESP8266/test/ir_Tcl_test.cpp @@ -1,4 +1,4 @@ -// Copyright 2019 David Conran +// Copyright 2019-2022 David Conran #include "ir_Tcl.h" #include "IRac.h" @@ -22,6 +22,13 @@ TEST(TestTcl112Ac, Housekeeping) { ASSERT_EQ(tcl_ac_remote_model_t::GZ055BE1, IRac::strToModel("GZ055BE1")); ASSERT_EQ(irutils::modelToStr(decode_type_t::TCL112AC, tcl_ac_remote_model_t::GZ055BE1), "GZ055BE1"); + + ASSERT_EQ("TCL96AC", typeToString(decode_type_t::TCL96AC)); + ASSERT_EQ(decode_type_t::TCL96AC, strToDecodeType("TCL96AC")); + ASSERT_TRUE(hasACState(decode_type_t::TCL96AC)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::TCL96AC)); + ASSERT_EQ(kTcl96AcBits, IRsend::defaultBits(decode_type_t::TCL96AC)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::TCL96AC)); } // Tests for decodeTcl112Ac(). @@ -703,3 +710,64 @@ TEST(TestTcl112AcClass, Timers) { "On Timer: Off, Off Timer: 02:00", ac.toString()); } + +// Decode a real Tcl96Ac A/C example from Issue #619 +TEST(TestDecodeTcl96Ac, DecodeRealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + // Tcl96Ac A/C example from Issue #1810 row_data.txt + const uint16_t rawData[99] = { + 1056, 550, + 608, 2182, 608, 1444, 606, 840, 608, 2182, + 608, 360, 612, 2182, 608, 356, 616, 1446, + 608, 354, 618, 366, 608, 366, 606, 356, + 618, 356, 618, 838, 608, 364, 608, 364, + 608, 2182, 608, 360, 612, 840, 608, 838, + 610, 364, 608, 360, 612, 2182, 608, 838, + 608, 838, 608, 2182, 608, 366, 606, 1444, + 608, 358, 614, 1444, 608, 838, 608, 366, + 606, 368, 606, 366, 606, 366, 606, 366, + 608, 364, 608, 342, 632, 840, 606, 340, + 606, 364, 634, 338, 634, 340, 632, 340, + 634, 814, 632, 814, 632, 2156, 634, 2156, + 634}; // UNKNOWN AE10E0CB + + const uint8_t expectedState[kTcl96AcStateLength] = { + 0xB6, 0x23, 0x00, 0x10, 0x85, 0x09, 0x63, 0x34, 0x00, 0x04, 0x00, 0x5A}; + + irsend.sendRaw(rawData, 99, 38000); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TCL96AC, irsend.capture.decode_type); + EXPECT_EQ(kTcl96AcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); +} + +// Decode a synthetic Tcl96Ac A/C message +TEST(TestDecodeTcl96Ac, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + irsend.reset(); + + const uint8_t expectedState[kTcl96AcStateLength] = { + 0xB6, 0x23, 0x00, 0x10, 0x85, 0x09, 0x63, 0x34, 0x00, 0x04, 0x00, 0x5A}; + + irsend.sendTcl96Ac(expectedState); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(TCL96AC, irsend.capture.decode_type); + EXPECT_EQ(kTcl96AcBits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); +} diff --git a/lib/IRremoteESP8266/test/ir_Toto_test.cpp b/lib/IRremoteESP8266/test/ir_Toto_test.cpp new file mode 100644 index 000000000..050bd01e6 --- /dev/null +++ b/lib/IRremoteESP8266/test/ir_Toto_test.cpp @@ -0,0 +1,159 @@ +// Copyright 2022 crankyoldgit (David Conran) + +#include "IRac.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "IRutils.h" +#include "gtest/gtest.h" + + +// General housekeeping +TEST(TestToto, Housekeeping) { + ASSERT_EQ("TOTO", typeToString(TOTO)); + ASSERT_FALSE(hasACState(TOTO)); + ASSERT_EQ(kTotoBits, IRsend::defaultBits(decode_type_t::TOTO)); + ASSERT_EQ(kSingleRepeat, IRsend::minRepeats(decode_type_t::TOTO)); +} + +// Tests for decodeToto(). + +// Decode normal Toto messages. +TEST(TestDecodeToto, SyntheticShortDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + // Short Toto 24-bit message. + irsend.reset(); + irsend.sendToto(0x0D0D00); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(TOTO, irsend.capture.decode_type); + EXPECT_EQ(kTotoBits, irsend.capture.bits); + EXPECT_EQ(0x0D0D00, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0x0D00, irsend.capture.command); +} + +// Decode real example via Issue #1806 +TEST(TestDecodeToto, RealShortDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + // Toto Full Flush from Issue #1806 + const uint16_t rawData[163] = { + 6266, 2734, + 598, 540, 598, 1626, 598, 512, 622, 516, 598, 514, 598, 510, 598, 514, + 628, 512, 596, 514, 600, 512, 598, 538, 600, 1622, 600, 512, 598, 540, + 602, 510, 598, 512, 598, 512, 624, 514, 598, 512, 598, 512, 598, 514, + 624, 512, 598, 514, 598, 1652, 596, 514, 598, 1626, 598, 1650, 598, 514, + 598, 512, 598, 540, 598, 514, 596, 1626, 626, 512, 574, 1648, 598, 1650, + 598, 514, 598, 512, 594, 544, 596, 514, + 598, 37996, + 6182, 2764, + 598, 514, 600, 1648, 600, 512, 596, 514, 598, 540, 598, 512, 600, 512, + 598, 512, 624, 514, 598, 514, 598, 512, 596, 1652, 598, 514, 598, 512, + 596, 540, 598, 514, 598, 512, 598, 512, 598, 540, 596, 516, 596, 514, 598, + 512, 574, 564, 598, 1626, 568, 542, 624, 1624, 626, 1622, 598, 514, 596, + 514, 598, 514, 596, 540, 600, 1622, 598, 512, 600, 1650, 598, 1624, 596, + 540, 600, 512, 598, 514, 596, 514, + 622}; // UNKNOWN 43BD67B3 + + irsend.sendRaw(rawData, 163, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(TOTO, irsend.capture.decode_type); + EXPECT_EQ(kTotoBits, irsend.capture.bits); + EXPECT_EQ(0x0D0D00, irsend.capture.value); + EXPECT_EQ(0, irsend.capture.address); + EXPECT_EQ(0x0D00, irsend.capture.command); +} + +// Decode real example via Issue #1806 +TEST(TestDecodeToto, RealLongDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + // Oscillate Bidet Function from Issue #1806 + const uint16_t rawData[491] = { + 6262, 2738, + 600, 538, 596, 1624, 598, 512, 652, 486, 628, 484, 600, 510, 596, 516, + 650, 488, 622, 488, 626, 484, 598, 540, 600, 1624, 602, 510, 596, 540, + 602, 510, 598, 514, 598, 512, 626, 512, 598, 512, 598, 512, 598, 514, + 626, 510, 598, 514, 598, 512, 598, 1650, 600, 1622, 600, 538, 596, 514, + 600, 510, 596, 514, 596, 542, 598, 514, 598, 1624, 626, 1622, 598, 512, + 600, 512, 620, 516, 598, 512, 598, 514, + 598, 40244, + 6184, 2764, + 598, 514, 598, 1650, 598, 514, 596, 516, 596, 542, 598, 512, 596, 516, + 600, 510, 624, 512, 598, 512, 596, 516, 598, 1652, 600, 512, 598, 512, + 598, 540, 596, 518, 594, 514, 598, 512, 596, 542, 598, 512, 596, 514, + 598, 512, 598, 540, 598, 514, 598, 1622, 624, 1626, 598, 514, 598, 512, + 626, 512, 596, 514, 596, 514, 596, 540, 598, 1624, 600, 1650, 600, 512, + 600, 510, 598, 512, 596, 540, 598, 512, + 600, 40244, + 6186, 2760, + 600, 512, 596, 1626, 624, 514, 598, 512, 600, 512, 598, 512, 626, 512, + 598, 512, 598, 512, 596, 540, 600, 510, 600, 1622, 600, 538, 596, 514, + 596, 514, 598, 514, 624, 514, 596, 516, 596, 514, 600, 512, 622, 514, 600, + 512, 600, 510, 600, 538, 602, 1622, 600, 1648, 626, 512, 572, 512, 598, + 514, 650, 488, 598, 512, 594, 516, 598, 1652, 598, 1626, 598, 514, 624, + 514, 596, 512, 600, 512, 598, 540, + 596, 40246, + 6184, 2736, + 598, 538, 598, 1624, 598, 512, 598, 540, 598, 512, 600, 512, 596, 514, + 600, 538, 626, 486, 598, 514, 596, 514, 652, 1596, 594, 516, 624, 514, + 594, 516, 598, 514, 622, 490, 596, 540, 596, 514, 624, 488, 594, 514, 598, + 538, 598, 512, 598, 514, 594, 516, 596, 540, 600, 1622, 572, 566, 600, + 512, 570, 542, 598, 512, 598, 540, 600, 512, 594, 516, 598, 1652, 600, + 512, 598, 514, 596, 514, 624, 514, + 598, 42468, + 6182, 2764, + 624, 490, 594, 1652, 600, 512, 596, 514, 596, 540, 600, 512, 598, 516, + 596, 514, 598, 538, 598, 516, 598, 512, 570, 1678, 596, 514, 596, 514, + 598, 512, 624, 512, 594, 516, 596, 514, 600, 510, 626, 512, 596, 516, + 594, 516, 598, 538, 598, 512, 596, 514, 596, 514, 596, 1652, 600, 514, + 594, 516, 622, 514, 600, 512, 598, 514, 596, 514, 624, 514, 596, 1626, + 598, 514, 622, 516, 594, 516, 598, 514, + 596, 42496, + 6184, 2764, + 596, 516, 598, 1624, 626, 512, 598, 512, 596, 516, 600, 512, 624, 514, + 598, 514, 594, 516, 596, 516, 620, 516, 600, 1624, 596, 540, 598, 514, + 600, 512, 596, 516, 596, 542, 594, 516, 598, 514, 594, 516, 624, 512, 598, + 512, 596, 514, 596, 514, 624, 514, 596, 516, 598, 1650, 594, 518, 596, + 514, 596, 516, 596, 542, 596, 514, 596, 516, 596, 514, 596, 1652, 598, + 514, 596, 514, 624, 514, 622, 488, 596}; // UNKNOWN 4AC5E8B3 + + + irsend.sendRaw(rawData, 491, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(TOTO, irsend.capture.decode_type); + EXPECT_EQ(kTotoLongBits, irsend.capture.bits); + EXPECT_EQ(0x60600080800, irsend.capture.value); + EXPECT_EQ(0x0600, irsend.capture.address); + EXPECT_EQ(0x0800, irsend.capture.command); +} + +TEST(TestDecodeToto, SyntheticLongDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + // Long Toto 48-bit message. + irsend.reset(); + irsend.sendToto(0x60600080800, kTotoLongBits); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(TOTO, irsend.capture.decode_type); + EXPECT_EQ(kTotoLongBits, irsend.capture.bits); + EXPECT_EQ(0x60600080800, irsend.capture.value); + EXPECT_EQ(0x0600, irsend.capture.address); + EXPECT_EQ(0x0800, irsend.capture.command); +} diff --git a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py index e061d3c42..69149215d 100644 --- a/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py +++ b/lib/IRremoteESP8266/tools/auto_analyse_raw_data.py @@ -100,7 +100,6 @@ class RawIRMessage(): def add_data_code(self, bin_str, name="", footer=True): """Add the common "data" sequence of code to send the bulk of a message.""" - # pylint: disable=no-self-use code = [] nbits = len(bin_str) code.append(f" // Data Section #{self.section_count}") @@ -115,7 +114,6 @@ class RawIRMessage(): def add_data_decode_code(self, bin_str, name="", footer=True): """Add the common "data" sequence code to decode the bulk of a message.""" - # pylint: disable=no-self-use code = [] nbits = len(bin_str) code.extend([ @@ -139,7 +137,6 @@ class RawIRMessage(): def add_data_byte_code(self, bin_str, name="", ambles=None): """Add the code to send the data from an array.""" - # pylint: disable=no-self-use code = [] nbits = len(bin_str) nbytes = nbits / 8 @@ -173,7 +170,6 @@ class RawIRMessage(): def add_data_byte_decode_code(self, bin_str, name="", ambles=None): """Add the common byte-wise "data" sequence decode code.""" - # pylint: disable=no-self-use code = [] nbits = len(bin_str) nbytes = nbits / 8 diff --git a/lib/IRremoteESP8266/tools/code_to_raw_test.sh b/lib/IRremoteESP8266/tools/code_to_raw_test.sh index fd587f2ad..c95b3da8b 100644 --- a/lib/IRremoteESP8266/tools/code_to_raw_test.sh +++ b/lib/IRremoteESP8266/tools/code_to_raw_test.sh @@ -56,7 +56,7 @@ unittest_success "${CODE_TO_RAW} --protocol SAMSUNG --code 0xE0E09966" "${OUT}" read -r -d '' OUT << xEOMx Code type: 18 (KELVINATOR) Code bits: 128 -Description: Power: On, Mode: 1 (Cool), Temp: 27C, Fan: 1 (Low), Turbo: Off, Quiet: Off, XFan: On, Ion: Off, Light: Off, Swing(H): Off, Swing(V): Off +Description: Power: On, Mode: 1 (Cool), Temp: 27C, Fan: 1 (Low), Turbo: Off, Quiet: Off, XFan: On, Ion: Off, Light: Off, Swing(H): Off, Swing(V): 0 (Off) uint16_t rawData[280] = {9010, 4504, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 19974, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 39950, 9010, 4504, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 19974, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 510, 680, 1530, 680, 1530, 680, 1530, 680, 1530, 680, 39950 }; // KELVINATOR uint8_t state[16] = {0x19, 0x0B, 0x80, 0x50, 0x00, 0x00, 0x00, 0xE0, 0x19, 0x0B, 0x80, 0x70, 0x00, 0x00, 0x10, 0xF0}; diff --git a/lib/IRremoteESP8266/tools/mkkeywords b/lib/IRremoteESP8266/tools/mkkeywords index 34def1332..af4314f19 100644 --- a/lib/IRremoteESP8266/tools/mkkeywords +++ b/lib/IRremoteESP8266/tools/mkkeywords @@ -32,8 +32,8 @@ EndOfTextEndOfTextEndOfText CLASSES=$(egrep -h \ "^ *((enum|class|struct) [a-zA-Z0-9_]+|} [a-zA-Z0-9_]+_t;$)" src/*.h | - sed 's/^ *//;s/enum class//;s/\;$//' | cut -d' ' -f2 | sort -u | - grep -v "^__") + sed 's/^ *//;s/enum class//;s/\;$//' | cut -d' ' -f2 | sed 's/:$//' | + sort -u | grep -v "^__") for i in ${CLASSES}; do echo -e "${i}\tKEYWORD1" done | sort -du diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index e9f7646ea..024d85ae1 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -56,6 +56,7 @@ build_flags = -DNDEBUG -DPUYA_SUPPORT=1 -DCORE_POST_2_5_0 -fno-strict-aliasing + -DLIBRARIES_NO_LOG=1 lib_ignore = ${esp82xx_defaults.lib_ignore} [esp82xx_2_6_x] @@ -220,9 +221,11 @@ platform = https://github.com/tasmota/platform-espressif32/re platform_packages = build_flags = -DESP32_STAGE -DMUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + -DLIBRARIES_NO_LOG=1 [core_esp32_stage] platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4.1/platform-espressif32-2.0.4.1.zip platform_packages = build_flags = -DESP32_STAGE -DMUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + -DLIBRARIES_NO_LOG=1 diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 1fd28d9bd..709cc2643 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -19,6 +19,7 @@ lib_ignore = ESP8266Ping [esp32_base] extends = common, core_esp32_IDF4_4__2_0_4 lib_deps = + Adafruit ILI9341 ESPEasy Adafruit_ST77xx Adafruit TSL2591 Library Adafruit NeoPixel @@ -30,18 +31,19 @@ lib_deps = LOLIN_EPD Makuna/NeoPixelBus @ 2.6.9 Multi Channel Relay Arduino Library - ShiftRegister74HC595_NonTemplate SparkFun ADXL345 Arduino Library SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9 VL53L0X @ 1.3.0 adafruit/Adafruit BusIO @ ^1.10.0 adafruit/Adafruit GFX Library@^1.11.1 - adafruit/Adafruit ILI9341@^1.5.10 ccronexpr rlogiacco/CircularBuffer td-er/ESPeasySerial @ 2.0.13 - td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1 + sparkfun/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.3 + ShiftRegister74HC595_NonTemplate + VL53L0X @ 1.3.0 RAK12019_LTR390_UV_Light + Adafruit NeoMatrix extra_scripts = post:tools/pio/post_esp32.py ${extra_scripts_default.extra_scripts} build_unflags = -Wall @@ -98,7 +100,8 @@ build_flags = ${esp32_base.build_flags} -DCOLLECTION_USE_RTTTL lib_deps = ${esp32_base.lib_deps} LittleFS - +extra_scripts = ${esp32_base.extra_scripts} + pre:tools/pio/ir_build_check.py [esp32_custom_base] @@ -140,6 +143,14 @@ lib_ignore = ${esp32_always.lib_ignore} HeatpumpIR extra_scripts = ${esp32_base.extra_scripts} pre:tools/pio/pre_custom_esp32.py + pre:tools/pio/ir_build_check.py + +[env:custom_ESP32_4M2M_NO_OTA_LittleFS] +extends = esp32_custom_base_LittleFS +board = esp32_4M +build_flags = ${esp32_custom_base_LittleFS.build_flags} + -DNO_HTTP_UPDATER +board_build.partitions = esp32_partition_app1810k_spiffs2172k.csv [env:normal_ESP32_4M316k] @@ -253,6 +264,7 @@ lib_deps = ${esp32_common.lib_deps} ServoESP32 build_flags = ${esp32_common.build_flags} -DFEATURE_ARDUINO_OTA=1 + -DFEATURE_SD=1 -D PLUGIN_NEOPIXEL_COLLECTION diff --git a/platformio_esp32s2_envs.ini b/platformio_esp32s2_envs.ini index 010408322..625150e72 100644 --- a/platformio_esp32s2_envs.ini +++ b/platformio_esp32s2_envs.ini @@ -40,6 +40,7 @@ lib_deps = ${esp32s2_common.lib_deps} ServoESP32 build_flags = ${esp32s2_common.build_flags} -DFEATURE_ARDUINO_OTA=1 + -DFEATURE_SD=1 -DPLUGIN_NEOPIXEL_COLLECTION @@ -54,6 +55,7 @@ lib_ignore = ${esp32_always.lib_ignore} HeatpumpIR extra_scripts = ${esp32s2_common.extra_scripts} pre:tools/pio/pre_custom_esp32.py + pre:tools/pio/ir_build_check.py diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index b645bad4b..5a7d281ed 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -51,22 +51,27 @@ lib_ignore = ${no_ir.lib_ignore} ${no_littlefs.lib_ignore} [esp82xx_common] -extends = common +lib_archive = ${common.lib_archive} +framework = ${common.framework} +upload_speed = ${common.upload_speed} +monitor_speed = ${common.monitor_speed} +targets = ${common.targets} build_flags = ${debug_flags.build_flags} ${mqtt_flags.build_flags} -DHTTPCLIENT_1_1_COMPATIBLE=0 build_unflags = -DDEBUG_ESP_PORT -fexceptions lib_deps = + Adafruit ILI9341 ESPEasy Adafruit_ST77xx Adafruit TSL2591 Library Adafruit NeoPixel ESP8266HTTPUpdateServer ESP8266WebServer - FrogmoreScd30 I2Cdevlib-Core ITG3205 LOLIN_EPD + FrogmoreScd30 Makuna/NeoPixelBus @ 2.6.9 Multi Channel Relay Arduino Library ShiftRegister74HC595_NonTemplate @@ -76,13 +81,14 @@ lib_deps = ccronexpr adafruit/Adafruit BusIO @ ^1.10.0 adafruit/Adafruit GFX Library@^1.11.1 - adafruit/Adafruit ILI9341@^1.5.10 bblanchon/ArduinoJson @ ^6.17.2 rlogiacco/CircularBuffer td-er/ESPeasySerial @ 2.0.13 td-er/RABurton ESP8266 Mutex @ ^1.0.2 - td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1 + sparkfun/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.3 + VL53L0X @ 1.3.0 RAK12019_LTR390_UV_Light + Adafruit NeoMatrix lib_ignore = ${esp82xx_defaults.lib_ignore} IRremoteESP8266 HeatpumpIR @@ -236,11 +242,13 @@ build_flags = -DBUILD_NO_DEBUG -Os -s ${esp82xx_common.build_flags} +extra_scripts = ${esp82xx_common.extra_scripts} [esp8266_1M] extends = esp82xx_1M board = esp8266_1M128k build_flags = ${esp82xx_1M.build_flags} +extra_scripts = ${esp82xx_1M.extra_scripts} ;;; Minimal *********************************************************** @@ -284,11 +292,13 @@ build_flags = ${esp82xx_common.build_flags} extends = esp82xx_common board = esp8266_4M1M_board build_flags = ${esp82xx_common.build_flags} +extra_scripts = ${esp82xx_common.extra_scripts} [esp8266_4M2M] extends = esp82xx_common board = esp8266_4M2M_board build_flags = ${esp82xx_common.build_flags} +extra_scripts = ${esp82xx_common.extra_scripts} diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini index a14911222..557b49a7c 100644 --- a/platformio_esp82xx_envs.ini +++ b/platformio_esp82xx_envs.ini @@ -45,14 +45,16 @@ lib_ignore = ESP32_ping ESP32WebServer ServoESP32 ESP32HTTPUpdateServer - adafruit/Adafruit GFX Library + adafruit/Adafruit GFX Library@^1.11.1 LOLIN_EPD - adafruit/Adafruit ILI9341 + Adafruit ILI9341 ESPEasy adafruit/Adafruit BusIO Adafruit NeoPixel Adafruit Motor Shield V2 Library Adafruit_ST77xx + Adafruit NeoMatrix extra_scripts = ${esp8266_custom_common.extra_scripts} + pre:tools/pio/ir_build_check.py @@ -110,14 +112,16 @@ lib_ignore = ESP32_ping ESP32WebServer ServoESP32 ESP32HTTPUpdateServer - adafruit/Adafruit GFX Library + adafruit/Adafruit GFX Library@^1.11.1 LOLIN_EPD - adafruit/Adafruit ILI9341 + Adafruit ILI9341 ESPEasy adafruit/Adafruit BusIO Adafruit NeoPixel Adafruit Motor Shield V2 Library Adafruit_ST77xx + Adafruit NeoMatrix extra_scripts = ${esp8266_custom_common.extra_scripts} + pre:tools/pio/ir_build_check.py ; TD-er: Disabled as it will no longer fit @@ -430,6 +434,8 @@ lib_ignore = ${ir.lib_ignore} build_flags = ${minimal_ir_extended.build_flags} ${esp8266_1M.build_flags} build_unflags = ${esp8266_1M_OTA.build_unflags} -DPLUGIN_BUILD_NORMAL_IR +extra_scripts = ${esp8266_1M.extra_scripts} + pre:tools/pio/ir_build_check.py ; Minimal IR: 4096k version -------------------------- @@ -443,6 +449,8 @@ platform_packages = ${ir.platform_packages} lib_ignore = ${ir.lib_ignore} build_flags = ${minimal_ir_extended.build_flags} ${esp8266_4M1M.build_flags} +extra_scripts = ${esp8266_4M1M.extra_scripts} + pre:tools/pio/ir_build_check.py ; 4M flash, 2M SPIFFS [env:minimal_IRext_ESP8266_4M2M] @@ -452,6 +460,8 @@ platform_packages = ${ir.platform_packages} lib_ignore = ${ir.lib_ignore} build_flags = ${minimal_ir_extended.build_flags} ${esp8266_4M2M.build_flags} +extra_scripts = ${esp8266_4M2M.extra_scripts} + pre:tools/pio/ir_build_check.py ; NORMAL IR: 4096k version -------------------------- @@ -466,6 +476,8 @@ lib_ignore = ${ir.lib_ignore} build_flags = ${normal_ir_extended_no_rx.build_flags} ${esp8266_4M2M.build_flags} -DLIMIT_BUILD_SIZE +extra_scripts = ${esp8266_4M2M.extra_scripts} + pre:tools/pio/ir_build_check.py @@ -720,6 +732,7 @@ platform_packages = ${regular_platform.platform_packages} build_flags = ${regular_platform.build_flags} ${esp8266_4M1M.build_flags} -D PLUGIN_NEOPIXEL_COLLECTION + -D LIMIT_BUILD_SIZE ; neopixel : 1024k version ---------------------------- [env:neopixel_ESP8266_1M_Domoticz] diff --git a/platformio_special_envs.ini b/platformio_special_envs.ini index ad53dfe00..82f452718 100644 --- a/platformio_special_envs.ini +++ b/platformio_special_envs.ini @@ -41,12 +41,12 @@ build_flags = ${regular_platform.build_flags} ${esp8266_4M1M.build_flags} -DPLUGIN_BUILD_CUSTOM lib_ignore = ESP32_ping - ESP32WebServer - ServoESP32 + ESP32WebServer + ServoESP32 ESP32HTTPUpdateServer - adafruit/Adafruit GFX Library + adafruit/Adafruit GFX Library@^1.11.1 LOLIN_EPD - adafruit/Adafruit ILI9341 + Adafruit ILI9341 adafruit/Adafruit BusIO Adafruit NeoPixel Adafruit Motor Shield V2 Library diff --git a/src/Custom-sample.h b/src/Custom-sample.h index 4f1781219..9601723da 100644 --- a/src/Custom-sample.h +++ b/src/Custom-sample.h @@ -448,6 +448,8 @@ static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = { // #define USES_P125 // ADXL345 SPI Acceleration / Gravity // #define USES_P126 // 74HC595 Shift register // #define USES_P127 // CDM7160 +// #define USES_P129 // 74HC165 Input shiftregisters +// #define USES_P131 // NeoMatrix // #define USES_P132 // INA3221 // #define USES_P133 // LTR390 UV @@ -464,6 +466,7 @@ static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = { // Special plugins needing IR library // #define USES_P016 // IR // #define P016_SEND_IR_TO_CONTROLLER false //IF true then the JSON replay solution is transmited back to the condroller. +// #define P016_FEATURE_COMMAND_HANDLING 0 // By default set to 1 to have the command table, that can be dsabled here // #define USES_P035 // IRTX // #define P016_P035_Extended_AC // The following define is needed for extended decoding of A/C Messages and or using standardised //common arguments for controlling all deeply supported A/C units diff --git a/src/CustomIR-sample.h b/src/CustomIR-sample.h new file mode 100644 index 000000000..6195b41d9 --- /dev/null +++ b/src/CustomIR-sample.h @@ -0,0 +1,268 @@ +#ifndef _CUSTOM_IR_H +#define _CUSTOM_IR_H + +/** + * Custom_IR-sample.h + * Usage: + * - Copy this file to CustomIR.h (CustomIR.h is excluded from Git, so don't try to commit that) + * - Change the supported list of IR devices by commenting/uncommenting options that DISABLE a device when UNcommented + */ + +// *** This file must be updated adding new supported devices when updating IRreceive8266 library *** + +// Set flags to enable (1) or disable (0) the DECODE_ and/or SEND_ feature for a specific IR device +// To limit ESPEasy build-size you can disable DECODE_ or SEND_ flags for devices not needed + +// SEND-ONLY protocols: +// #define SEND_GLOBALCACHE 0 // Is used by many sending protocols, so should probably be left to default +// #define SEND_PRONTO 0 +// #define SEND_RAW 0 // SEND_RAW support should be left to default unless explicitly disabled via P016_P035_USE_RAW_RAW2 +// #define SEND_SHERWOOD 0 +// UNUSED (exception) // #define SEND_SONY_38K 0 // is enabled/disabled via SEND_SONY + +// Standard: Use defaults for up to library version 2.8.2 +// Change as desired after copying CustomIR-sample.h to CustomIR.h + +// #define DECODE_RC5 0 +// #define SEND_RC5 0 +// #define DECODE_RC6 0 +// #define SEND_RC6 0 +// #define DECODE_NEC 0 +// #define SEND_NEC 0 +// #define DECODE_SONY 0 +// #define SEND_SONY 0 +// #define DECODE_PANASONIC 0 +// #define SEND_PANASONIC 0 +// #define DECODE_JVC 0 +// #define SEND_JVC 0 +// #define DECODE_SAMSUNG 0 +// #define SEND_SAMSUNG 0 +// #define DECODE_WHYNTER 0 +// #define SEND_WHYNTER 0 +// #define DECODE_AIWA_RC_T501 0 +// #define SEND_AIWA_RC_T501 0 +// #define DECODE_LG 0 +// #define SEND_LG 0 +// #define DECODE_SANYO 0 +// #define SEND_SANYO 0 +// #define DECODE_MITSUBISHI 0 +// #define SEND_MITSUBISHI 0 +// #define DECODE_DISH 0 +// #define SEND_DISH 0 +// #define DECODE_SHARP 0 +// #define SEND_SHARP 0 +// #define DECODE_COOLIX 0 +// #define SEND_COOLIX 0 +// #define DECODE_DAIKIN 0 +// #define SEND_DAIKIN 0 +// #define DECODE_DENON 0 +// #define SEND_DENON 0 +// #define DECODE_KELVINATOR 0 +// #define SEND_KELVINATOR 0 +// #define DECODE_MITSUBISHI_AC 0 +// #define SEND_MITSUBISHI_AC 0 +// #define DECODE_RCMM 0 +// #define SEND_RCMM 0 +// #define DECODE_SANYO_LC7461 0 +// #define SEND_SANYO_LC7461 0 +// #define DECODE_RC5X 0 +// #define SEND_RC5X 0 +// #define DECODE_GREE 0 +// #define SEND_GREE 0 +// #define DECODE_NEC_LIKE 0 +// #define SEND_NEC_LIKE 0 +// #define DECODE_ARGO 0 +// #define SEND_ARGO 0 +// #define DECODE_TROTEC 0 +// #define SEND_TROTEC 0 +// #define DECODE_NIKAI 0 +// #define SEND_NIKAI 0 +// #define DECODE_TOSHIBA_AC 0 +// #define SEND_TOSHIBA_AC 0 +// #define DECODE_FUJITSU_AC 0 +// #define SEND_FUJITSU_AC 0 +// #define DECODE_MIDEA 0 +// #define SEND_MIDEA 0 +// #define DECODE_MAGIQUEST 0 +// #define SEND_MAGIQUEST 0 +// #define DECODE_LASERTAG 0 +// #define SEND_LASERTAG 0 +// #define DECODE_CARRIER_AC 0 +// #define SEND_CARRIER_AC 0 +// #define DECODE_HAIER_AC 0 +// #define SEND_HAIER_AC 0 +// #define DECODE_MITSUBISHI2 0 +// #define SEND_MITSUBISHI2 0 +// #define DECODE_HITACHI_AC 0 +// #define SEND_HITACHI_AC 0 +// #define DECODE_HITACHI_AC1 0 +// #define SEND_HITACHI_AC1 0 +// #define DECODE_HITACHI_AC2 0 +// #define SEND_HITACHI_AC2 0 +// #define DECODE_GICABLE 0 +// #define SEND_GICABLE 0 +// #define DECODE_HAIER_AC_YRW02 0 +// #define SEND_HAIER_AC_YRW02 0 +// #define DECODE_WHIRLPOOL_AC 0 +// #define SEND_WHIRLPOOL_AC 0 +// #define DECODE_SAMSUNG_AC 0 +// #define SEND_SAMSUNG_AC 0 +// #define DECODE_LUTRON 0 +// #define SEND_LUTRON 0 +// #define DECODE_ELECTRA_AC 0 +// #define SEND_ELECTRA_AC 0 +// #define DECODE_PANASONIC_AC 0 +// #define SEND_PANASONIC_AC 0 +// #define DECODE_PIONEER 0 +// #define SEND_PIONEER 0 +// #define DECODE_LG2 0 +// #define SEND_LG2 0 +// #define DECODE_MWM 0 +// #define SEND_MWM 0 +// #define DECODE_DAIKIN2 0 +// #define SEND_DAIKIN2 0 +// #define DECODE_VESTEL_AC 0 +// #define SEND_VESTEL_AC 0 +// #define DECODE_TECO 0 +// #define SEND_TECO 0 +// #define DECODE_SAMSUNG36 0 +// #define SEND_SAMSUNG36 0 +// #define DECODE_TCL112AC 0 +// #define SEND_TCL112AC 0 +// #define DECODE_LEGOPF 0 +// #define SEND_LEGOPF 0 +// #define DECODE_MITSUBISHI_HEAVY_88 0 +// #define SEND_MITSUBISHI_HEAVY_88 0 +// #define DECODE_MITSUBISHI_HEAVY_152 0 +// #define SEND_MITSUBISHI_HEAVY_152 0 +// #define DECODE_DAIKIN216 0 +// #define SEND_DAIKIN216 0 +// #define DECODE_SHARP_AC 0 +// #define SEND_SHARP_AC 0 +// #define DECODE_GOODWEATHER 0 +// #define SEND_GOODWEATHER 0 +// #define DECODE_INAX 0 +// #define SEND_INAX 0 +// #define DECODE_DAIKIN160 0 +// #define SEND_DAIKIN160 0 +// #define DECODE_NEOCLIMA 0 +// #define SEND_NEOCLIMA 0 +// #define DECODE_DAIKIN176 0 +// #define SEND_DAIKIN176 0 +// #define DECODE_DAIKIN128 0 +// #define SEND_DAIKIN128 0 +// #define DECODE_AMCOR 0 +// #define SEND_AMCOR 0 +// #define DECODE_DAIKIN152 0 +// #define SEND_DAIKIN152 0 +// #define DECODE_MITSUBISHI136 0 +// #define SEND_MITSUBISHI136 0 +// #define DECODE_MITSUBISHI112 0 +// #define SEND_MITSUBISHI112 0 +// #define DECODE_HITACHI_AC424 0 +// #define SEND_HITACHI_AC424 0 +// #define DECODE_EPSON 0 +// #define SEND_EPSON 0 +// #define DECODE_SYMPHONY 0 +// #define SEND_SYMPHONY 0 +// #define DECODE_HITACHI_AC3 0 +// #define SEND_HITACHI_AC3 0 +// #define DECODE_DAIKIN64 0 +// #define SEND_DAIKIN64 0 +// #define DECODE_AIRWELL 0 +// #define SEND_AIRWELL 0 +// #define DECODE_DELONGHI_AC 0 +// #define SEND_DELONGHI_AC 0 +// #define DECODE_DOSHISHA 0 +// #define SEND_DOSHISHA 0 +// #define DECODE_MULTIBRACKETS 0 +// #define SEND_MULTIBRACKETS 0 +// #define DECODE_CARRIER_AC40 0 +// #define SEND_CARRIER_AC40 0 +// #define DECODE_CARRIER_AC64 0 +// #define SEND_CARRIER_AC64 0 +// #define DECODE_HITACHI_AC344 0 +// #define SEND_HITACHI_AC344 0 +// #define DECODE_CORONA_AC 0 +// #define SEND_CORONA_AC 0 +// #define DECODE_MIDEA24 0 +// #define SEND_MIDEA24 0 +// #define DECODE_ZEPEAL 0 +// #define SEND_ZEPEAL 0 +// #define DECODE_SANYO_AC 0 +// #define SEND_SANYO_AC 0 +// #define DECODE_VOLTAS 0 +// #define SEND_VOLTAS 0 +// #define DECODE_METZ 0 +// #define SEND_METZ 0 +// #define DECODE_TRANSCOLD 0 +// #define SEND_TRANSCOLD 0 +// #define DECODE_TECHNIBEL_AC 0 +// #define SEND_TECHNIBEL_AC 0 +// #define DECODE_MIRAGE 0 +// #define SEND_MIRAGE 0 +// #define DECODE_ELITESCREENS 0 +// #define SEND_ELITESCREENS 0 +// #define DECODE_PANASONIC_AC32 0 +// #define SEND_PANASONIC_AC32 0 +// #define DECODE_MILESTAG2 0 +// #define SEND_MILESTAG2 0 +// #define DECODE_ECOCLIM 0 +// #define SEND_ECOCLIM 0 +// #define DECODE_XMP 0 +// #define SEND_XMP 0 +// #define DECODE_TRUMA 0 +// #define SEND_TRUMA 0 +// #define DECODE_HAIER_AC176 0 +// #define SEND_HAIER_AC176 0 +// #define DECODE_TEKNOPOINT 0 +// #define SEND_TEKNOPOINT 0 +// #define DECODE_KELON 0 +// #define SEND_KELON 0 +// #define DECODE_TROTEC_3550 0 +// #define SEND_TROTEC_3550 0 +// #define DECODE_SANYO_AC88 0 +// #define SEND_SANYO_AC88 0 +// #define DECODE_BOSE 0 +// #define SEND_BOSE 0 +// #define DECODE_ARRIS 0 +// #define SEND_ARRIS 0 +// #define DECODE_RHOSS 0 +// #define SEND_RHOSS 0 +// #define DECODE_AIRTON 0 +// #define SEND_AIRTON 0 +// #define DECODE_COOLIX48 0 +// #define SEND_COOLIX48 0 + +// ATTENTION: DISABLE new devices from library version 2.8.2 and newer by default to save ESPEasy .bin size +// When extending this list: Add both the DECODE_ and SEND_ defines per new device! + +// Version 2.8.2 added devices: +#define DECODE_HITACHI_AC264 0 +#define SEND_HITACHI_AC264 0 +#define DECODE_KELON168 0 +#define SEND_KELON168 0 +#define DECODE_HITACHI_AC296 0 +#define SEND_HITACHI_AC296 0 +#define DECODE_DAIKIN200 0 +#define SEND_DAIKIN200 0 +#define DECODE_HAIER_AC160 0 +#define SEND_HAIER_AC160 0 +#define DECODE_CARRIER_AC128 0 +#define SEND_CARRIER_AC128 0 +#define DECODE_TOTO 0 +#define SEND_TOTO 0 +#define DECODE_CLIMABUTLER 0 +#define SEND_CLIMABUTLER 0 +#define DECODE_TCL96AC 0 +#define SEND_TCL96AC 0 +#define DECODE_BOSCH144 0 +#define SEND_BOSCH144 0 +#define DECODE_SANYO_AC152 0 +#define SEND_SANYO_AC152 0 +#define DECODE_DAIKIN312 0 +#define SEND_DAIKIN312 0 + +// Version 2.x.y added devices: + +#endif // ifndef _CUSTOM_IR_H diff --git a/src/_P016_IR.ino b/src/_P016_IR.ino index 12551640a..609f93da3 100644 --- a/src/_P016_IR.ino +++ b/src/_P016_IR.ino @@ -21,6 +21,13 @@ // If the IR library can encode those kind of messages then a JSON formated command will be given, that can be replayed by P035 as well. // That commands format is: // IRSENDAC,'{"protocol":"COOLIX","power":"on","mode":"dry","fanspeed":"auto","temp":22,"swingv":"max","swingh":"off"}' + +/** Changelog: + * 2022-08-08 tonhuisman: Optionally (compile-time) disable command handling by setting #define P016_FEATURE_COMMAND_HDNLING 0 + * Make reserved buffer size for receiver configurable 100..1024 uint16_t = 200-2048 bytes + * Change UI to show buffer size in bytes instead of 'units' to avoid confusion. + * 2022-08-08 tonhuisman: Add Changelog, older changes not clearly registered, add newer changelog lines on the top of this list. + */ # include # include # include @@ -41,6 +48,7 @@ # define PLUGIN_NAME_016 "Communication - IR Receive (TSOP4838)" # define PLUGIN_VALUENAME1_016 "IR" # define P016_CMDINHIBIT PCONFIG(1) +# define P016_BUFFERSIZE PCONFIG(2) # define P016_SETTINGS_VERSION PCONFIG(7) // 0 = V1, 2 = V2 # ifndef P016_SEND_IR_TO_CONTROLLER @@ -48,6 +56,8 @@ # endif // ifndef P016_SEND_IR_TO_CONTROLLER // History +// @tonhuisman: 2022-08-08 +// FIX: Resolve high memory use bu having the default buffer size reduced from 1024 to 100, and make that a setting // @tonhuisman: 2021-08-05 // FIX: Resolve stack size issues by replacing 2 arrays by std::vectors // CHG: Remove unneeded #define for nr. of decoding types @@ -78,7 +88,7 @@ // ==================== start of TUNEABLE PARAMETERS ==================== // 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 kCaptureBufferSize = 1024; +// const uint16_t kCaptureBufferSize = 1024; // Replaced by setting P016_BUFFERSIZE // kTimeout is the Nr. of milli-Seconds of no-more-data before we consider a // message ended. @@ -143,9 +153,10 @@ boolean displayRawToReadableB32Hex(String& outputStr, decode_results results); # endif // ifdef P016_P035_USE_RAW_RAW2 # ifdef PLUGIN_016_DEBUG -void P016_infoLogMemory(const __FlashStringHelper * text) { +void P016_infoLogMemory(const __FlashStringHelper *text) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log; + if (log.reserve(40 + strlen_P((PGM_P)text))) { log += F("P016: Free memory "); log += text; @@ -228,29 +239,34 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) addLog(LOG_LEVEL_INFO, F("INIT: IR RX")); addLog(LOG_LEVEL_INFO, F("IR lib Version: " _IRREMOTEESP8266_VERSION_)); } - irReceiver = new (std::nothrow) IRrecv(irPin, kCaptureBufferSize, P016_TIMEOUT, true); + + uint16_t bufsize = P016_BUFFERSIZE; + + if ((bufsize < P016_MIN_BUFFERSIZE) || (bufsize > P016_MAX_BUFFERSIZE)) { bufsize = P016_DEFAULT_BUFFERSIZE; } // safety check + + irReceiver = new (std::nothrow) IRrecv(irPin, bufsize, P016_TIMEOUT, true); # ifdef PLUGIN_016_DEBUG addLog(LOG_LEVEL_INFO, F("P016_PLUGIN_INIT IR receiver created")); # endif // PLUGIN_016_DEBUG if (nullptr != irReceiver) { irReceiver->setUnknownThreshold(kMinUnknownSize); // Ignore messages with less than minimum on or off pulses. - irReceiver->enableIRIn(); // Start the receiver + irReceiver->enableIRIn(); // Start the receiver # ifdef PLUGIN_016_DEBUG addLog(LOG_LEVEL_INFO, F("P016_PLUGIN_INIT IR receiver initialized")); # endif // PLUGIN_016_DEBUG } } - if ((nullptr != irReceiver) && (irPin == -1)) - { - irReceiver->disableIRIn(); - delete irReceiver; - irReceiver = nullptr; - # ifdef PLUGIN_016_DEBUG - addLog(LOG_LEVEL_INFO, F("P016_PLUGIN_INIT IR receiver destroyed")); - # endif // PLUGIN_016_DEBUG - } + // if ((nullptr != irReceiver) && (irPin == -1)) // Unreachable code + // { + // irReceiver->disableIRIn(); + // delete irReceiver; + // irReceiver = nullptr; + // # ifdef PLUGIN_016_DEBUG + // addLog(LOG_LEVEL_INFO, F("P016_PLUGIN_INIT IR receiver destroyed")); + // # endif // PLUGIN_016_DEBUG + // } # ifdef PLUGIN_016_DEBUG addLog(LOG_LEVEL_INFO, F("P016_PLUGIN_INIT done")); @@ -288,6 +304,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) addLog(LOG_LEVEL_INFO, F("P016_PLUGIN_SET_DEFAULTS ...")); # endif // PLUGIN_016_DEBUG + P016_BUFFERSIZE = P016_DEFAULT_BUFFERSIZE; P016_SETTINGS_VERSION = P16_SETTINGS_LATEST; // New installs don't need conversion break; } @@ -302,16 +319,29 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) addRowLabel(F("Info")); addHtml(F("Check serial or web log for replay solutions via Communication - IR Transmit plugin")); + addFormNumericBox(F("Receiver buffer size"), F("pbuffersize"), P016_BUFFERSIZE * 2, P016_MIN_BUFFERSIZE * 2, P016_MAX_BUFFERSIZE * 2); + String unit; + unit += P016_MIN_BUFFERSIZE * 2; + unit += F(".."); + unit += P016_MAX_BUFFERSIZE * 2; + unit += F(" bytes"); + addUnit(unit); + addFormNote(F("Increase buffer size if IR commands are received incomplete.")); + + addFormSubHeader(F("Content")); + # if P016_FEATURE_COMMAND_HANDLING bool bAddNewCode = bitRead(PCONFIG_LONG(0), P016_BitAddNewCode); - addFormCheckBox(F("Add new received code to command lines"), F("p016_AddNewCode"), bAddNewCode); + addFormCheckBox(F("Add new received code to command lines"), F("pAddNewCode"), bAddNewCode); bool bExecuteCmd = bitRead(PCONFIG_LONG(0), P016_BitExecuteCmd); - addFormCheckBox(F("Execute commands"), F("p016_ExecuteCmd"), bExecuteCmd); + addFormCheckBox(F("Execute commands"), F("pExecuteCmd"), bExecuteCmd); + # endif // if P016_FEATURE_COMMAND_HANDLING bool bAcceptUnknownType = bitRead(PCONFIG_LONG(0), P016_BitAcceptUnknownType); - addFormCheckBox(F("Accept DecodeType UNKNOWN"), F("p016_AcceptUnknownType"), bAcceptUnknownType); + addFormCheckBox(F("Accept DecodeType UNKNOWN"), F("pAcceptUnknownType"), bAcceptUnknownType); + # if P016_FEATURE_COMMAND_HANDLING addFormNumericBox(F("Inhibit time for the same command [ms]"), - F("p016_cmdinhibit"), + F("pcmdinhibit"), P016_CMDINHIBIT, 1, 2000); @@ -322,26 +352,35 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) int size = static_cast(decode_type_t::kLastDecodeType) + 1; - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log; // Log this always - if (log.reserve(30)) { - log += F("IR: available decodetypes: "); - log += size; - addLogMove(LOG_LEVEL_INFO, log); - } - } - // Fill a vector with all supported decode_type_t names - std::vector decodeTypes; - std::vector decodeTypeOptions; + std::vectordecodeTypes; + std::vectordecodeTypeOptions; + + int protocolCount = 0; for (int i = 0; i < size; i++) { - decodeTypeOptions.push_back(i); - decodeTypes.push_back(typeToString(static_cast(i), false)); + const String protocol = typeToString(static_cast(i), false); + + if (protocol.length() > 1) { + decodeTypeOptions.push_back(i); + decodeTypes.push_back(protocol); + protocolCount++; + } // addLog(LOG_LEVEL_INFO, decodeTypes[i]); // For development debugging purposes delay(0); } + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + String log; // Log this always + + if (log.reserve(30)) { + log += F("IR: available decodetypes: "); + log += protocolCount; + addLogMove(LOG_LEVEL_INFO, log); + } + } + const String P016_HEX_INPUT_PATTERN = F("(0x)?[0-9a-fA-F]{0,16}"); // 16 nibbles = 64 bit, 0x prefix is allowed but not added by // default addRowLabel(F("Code - command map")); @@ -372,7 +411,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) addHtmlInt(varNr + 1); // # html_TD(); { // Decode type - addSelector(getPluginCustomArgName(rowCnt + 0), size, &decodeTypes[0], &decodeTypeOptions[0], nullptr, + addSelector(getPluginCustomArgName(rowCnt + 0), protocolCount, &decodeTypes[0], &decodeTypeOptions[0], nullptr, static_cast(line.CodeDecodeType), false, true, EMPTY_STRING); } html_TD(); @@ -387,7 +426,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) html_TD(); { - addSelector(getPluginCustomArgName(rowCnt + 3), size, &decodeTypes[0], &decodeTypeOptions[0], nullptr, + addSelector(getPluginCustomArgName(rowCnt + 3), protocolCount, &decodeTypes[0], &decodeTypeOptions[0], nullptr, static_cast(line.AlternativeCodeDecodeType), false, true, EMPTY_STRING); } html_TD(); @@ -420,6 +459,8 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) } } + # endif // if P016_FEATURE_COMMAND_HANDLING + # ifdef PLUGIN_016_DEBUG P016_infoLogMemory(F("after load")); addLog(LOG_LEVEL_INFO, F("P016_PLUGIN_WEBFORM_LOAD done")); @@ -441,81 +482,90 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + 10); uint32_t lSettings = 0; - bitWrite(lSettings, P016_BitAddNewCode, isFormItemChecked(F("p016_AddNewCode"))); - bitWrite(lSettings, P016_BitExecuteCmd, isFormItemChecked(F("p016_ExecuteCmd"))); - bitWrite(lSettings, P016_BitAcceptUnknownType, isFormItemChecked(F("p016_AcceptUnknownType"))); + # if P016_FEATURE_COMMAND_HANDLING + bitWrite(lSettings, P016_BitAddNewCode, isFormItemChecked(F("pAddNewCode"))); + bitWrite(lSettings, P016_BitExecuteCmd, isFormItemChecked(F("pExecuteCmd"))); + # endif // if P016_FEATURE_COMMAND_HANDLING + bitWrite(lSettings, P016_BitAcceptUnknownType, isFormItemChecked(F("pAcceptUnknownType"))); bEnableIRcodeAdding = true; PCONFIG_LONG(0) = lSettings; - P016_CMDINHIBIT = getFormItemInt(F("p016_cmdinhibit")); + # if P016_FEATURE_COMMAND_HANDLING + P016_CMDINHIBIT = getFormItemInt(F("pcmdinhibit")); + # endif // if P016_FEATURE_COMMAND_HANDLING + P016_BUFFERSIZE = ceil(getFormItemInt(F("pbuffersize")) / 2.0f); // UI shows bytes, we store buffer unit = uint16_t - # ifdef PLUGIN_016_DEBUG - P016_infoLogMemory(F("before save")); - # endif // ifdef PLUGIN_016_DEBUG + # if P016_FEATURE_COMMAND_HANDLING { - String strError; - strError.reserve(30); // Length of expected string, needed for strings > 11 chars + # ifdef PLUGIN_016_DEBUG + P016_infoLogMemory(F("before save")); + # endif // ifdef PLUGIN_016_DEBUG - int rowCnt = 0; + String strError; + strError.reserve(30); // Length of expected string, needed for strings > 11 chars - for (uint8_t varNr = 0; varNr < P16_Nlines; varNr++) { - tCommandLinesV2 line; + int rowCnt = 0; - strError.clear(); + for (uint8_t varNr = 0; varNr < P16_Nlines; varNr++) { + tCommandLinesV2 line; - // Normal Code & flags - line.CodeDecodeType = static_cast(getFormItemInt(getPluginCustomArgName(rowCnt + 0))); - bitWrite(line.CodeFlags, P16_FLAGS_REPEAT, isFormItemChecked(getPluginCustomArgName(rowCnt + 1))); - line.Code = 0; + strError.clear(); - char strCode[P16_Cchars] = {0}; - if (!safe_strncpy(strCode, webArg(getPluginCustomArgName(rowCnt + 2)), P16_Cchars)) { - strError += F("Code "); - strError += (varNr + 1); - strError += ' '; - } else { - line.Code = hexToULL(strCode); // convert string with hexnumbers to uint64_t - } + // Normal Code & flags + line.CodeDecodeType = static_cast(getFormItemInt(getPluginCustomArgName(rowCnt + 0))); + bitWrite(line.CodeFlags, P16_FLAGS_REPEAT, isFormItemChecked(getPluginCustomArgName(rowCnt + 1))); + line.Code = 0; - delay(0); + char strCode[P16_Cchars] = { 0 }; - // Alternate Code & flags - line.AlternativeCodeDecodeType = - static_cast(getFormItemInt(getPluginCustomArgName(rowCnt + 3))); - bitWrite(line.AlternativeCodeFlags, P16_FLAGS_REPEAT, - isFormItemChecked(getPluginCustomArgName(rowCnt + 4))); - line.AlternativeCode = 0; - - if (!safe_strncpy(strCode, webArg(getPluginCustomArgName(rowCnt + 5)), P16_Cchars)) { - strError += F("Alt.Code "); - strError += (varNr + 1); - strError += ' '; - } else { - line.AlternativeCode = hexToULL(strCode); // convert string with hexnumbers to uint64_t - } - - // Command - if (!safe_strncpy(line.Command, webArg(getPluginCustomArgName(rowCnt + 6)), P16_Nchars)) { - strError += F("Command "); - strError += (varNr + 1); - } - line.Command[P16_Nchars - 1] = 0; // Terminate string - - if (!strError.isEmpty()) { - addHtmlError(strError); - } - - rowCnt += 7; - delay(0); - - P016_data_struct::saveCommandLine(event, line, varNr); + if (!safe_strncpy(strCode, webArg(getPluginCustomArgName(rowCnt + 2)), P16_Cchars)) { + strError += F("Code "); + strError += (varNr + 1); + strError += ' '; + } else { + line.Code = hexToULL(strCode); // convert string with hexnumbers to uint64_t } - # ifdef PLUGIN_016_DEBUG - P016_infoLogMemory(F("after save")); - # endif // ifdef PLUGIN_016_DEBUG + delay(0); + + // Alternate Code & flags + line.AlternativeCodeDecodeType = + static_cast(getFormItemInt(getPluginCustomArgName(rowCnt + 3))); + bitWrite(line.AlternativeCodeFlags, P16_FLAGS_REPEAT, + isFormItemChecked(getPluginCustomArgName(rowCnt + 4))); + line.AlternativeCode = 0; + + if (!safe_strncpy(strCode, webArg(getPluginCustomArgName(rowCnt + 5)), P16_Cchars)) { + strError += F("Alt.Code "); + strError += (varNr + 1); + strError += ' '; + } else { + line.AlternativeCode = hexToULL(strCode); // convert string with hexnumbers to uint64_t + } + + // Command + if (!safe_strncpy(line.Command, webArg(getPluginCustomArgName(rowCnt + 6)), P16_Nchars)) { + strError += F("Command "); + strError += (varNr + 1); + } + line.Command[P16_Nchars - 1] = 0; // Terminate string + + if (!strError.isEmpty()) { + addHtmlError(strError); + } + + rowCnt += 7; + delay(0); + + P016_data_struct::saveCommandLine(event, line, varNr); + } + + # ifdef PLUGIN_016_DEBUG + P016_infoLogMemory(F("after save")); + # endif // ifdef PLUGIN_016_DEBUG } + # endif // if P016_FEATURE_COMMAND_HANDLING # ifdef PLUGIN_016_DEBUG addLog(LOG_LEVEL_INFO, F("P016_PLUGIN_WEBFORM_SAVE Done")); @@ -524,6 +574,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) break; } + # if P016_FEATURE_COMMAND_HANDLING case PLUGIN_ONCE_A_SECOND: { P016_data_struct *P016_data = @@ -533,14 +584,15 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) if (P016_data->bCodeChanged) { // code has been added -> SaveCustomTaskSettings P016_data->saveCommandLines(event); P016_data->bCodeChanged = false; - # ifdef PLUGIN_016_DEBUG + # ifdef PLUGIN_016_DEBUG addLog(LOG_LEVEL_INFO, F("P016_PLUGIN_ONCE_A_SECOND CustomTaskSettings Saved")); - # endif // PLUGIN_016_DEBUG + # endif // PLUGIN_016_DEBUG } } success = true; break; } + # endif // if P016_FEATURE_COMMAND_HANDLING case PLUGIN_TEN_PER_SECOND: { @@ -554,7 +606,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) { addLog(LOG_LEVEL_ERROR, F("IR: WARNING, IR code is too big for buffer. Try pressing the transmiter button only momenteraly")); success = false; - break; // Do not continue and risk hanging the ESP + break; // Do not continue and risk hanging the ESP } // Display the basic output of what we found. @@ -563,9 +615,11 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) { String output; output.reserve(100); // Length of expected string, needed for strings > 11 chars - // String output = String(F("IRSEND,")) + typeToString(results.decode_type, results.repeat) + ',' + resultToHexidecimal(&results) + // String output = String(F("IRSEND,")) + typeToString(results.decode_type, results.repeat) + ',' + + // resultToHexidecimal(&results) // + ',' + uint64ToString(results.bits); - // addLog(LOG_LEVEL_INFO, output); //Show the appropriate command to the user, so he can replay the message via P035 // Old style + // addLog(LOG_LEVEL_INFO, output); //Show the appropriate command to the user, so he can replay the message via P035 // Old + // style // command output += F("{\"protocol\":\""); output += typeToString(results.decode_type, results.repeat); @@ -577,6 +631,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) if (loglevelActiveFor(LOG_LEVEL_INFO)) { String Log; + if (Log.reserve(output.length() + 22)) { Log += F("IRSEND,\'"); Log += output; @@ -588,6 +643,8 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) event->String2 = std::move(output); } + # if P016_FEATURE_COMMAND_HANDLING + // Check if this is a code we have a command for or we have to add P016_data_struct *P016_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -605,7 +662,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) if (iCodeDecodeType == decode_type_t::UNKNOWN) { // set iCodeDecodeType UNKNOWN to RAW, otherwise AddCode() or ExecuteCode() will fail - iCodeDecodeType=decode_type_t::RAW; + iCodeDecodeType = decode_type_t::RAW; } if (bitRead(PCONFIG_LONG(0), P016_BitAddNewCode) && bEnableIRcodeAdding) { @@ -617,6 +674,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) } } } + # endif // if P016_FEATURE_COMMAND_HANDLING } if (!bitRead(PCONFIG_LONG(0), P016_BitAcceptUnknownType)) { @@ -631,13 +689,14 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) # endif // ifdef P016_P035_USE_RAW_RAW2 { addLog(LOG_LEVEL_INFO, - F("IR: No replay solutions found! Press button again or try RAW encoding (timings are in the serial output)")); + F("IR: No replay solutions found! Press button again or try RAW encoding (timings are in the serial output)")); serialPrint(F("IR: RAW TIMINGS: ")); serialPrint(resultToSourceCode(&results)); event->String2 = F("NaN"); yield(); // Feed the WDT as it can take a while to print. - // addLog(LOG_LEVEL_DEBUG,(String(F("IR: RAW TIMINGS: ")) + resultToSourceCode(&results))); // Output the results as RAW - // source code //not showing up nicely in the web log + // addLog(LOG_LEVEL_DEBUG,(String(F("IR: RAW TIMINGS: ")) + resultToSourceCode(&results))); // Output the results as + // RAW + // source code //not showing up nicely in the web log } } @@ -673,6 +732,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) if (loglevelActiveFor(LOG_LEVEL_INFO)) { // If we got a human-readable description of the message, display it. String log; + if (log.reserve(10 + description.length())) { log += F("AC State: "); log += description; @@ -681,8 +741,8 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) } } - if (IRac::isProtocolSupported(results.decode_type)) // Check If there is a replayable AC state and show the JSON command that can be - // send + if (IRac::isProtocolSupported(results.decode_type) && // Check If there is a replayable AC state and show the JSON command that can + (typeToString(results.decode_type).length() > 1)) // be sent { IRAcUtils::decodeToState(&results, &state); StaticJsonDocument<300> doc; @@ -754,6 +814,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) if (loglevelActiveFor(LOG_LEVEL_INFO)) { // Show the command that the user can put to replay the AC state with P035 String log; + if (log.reserve(12 + event->String2.length())) { log += F("IRSENDAC,'"); log += event->String2; @@ -800,7 +861,8 @@ boolean displayRawToReadableB32Hex(String& outputStr, decode_results results) { uint16_t div[2]; - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG + // print the values: either pulses or blanks if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { String line; @@ -810,7 +872,7 @@ boolean displayRawToReadableB32Hex(String& outputStr, decode_results results) } addLogMove(LOG_LEVEL_DEBUG, line); // Display the RAW timings } - #endif + # endif // ifndef BUILD_NO_DEBUG // Find a common denominator divisor for odd indexes (pulses) and then even indexes (blanks). for (uint16_t p = 0; p < 2; p++) @@ -877,7 +939,8 @@ boolean displayRawToReadableB32Hex(String& outputStr, decode_results results) } div[p] = bstDiv; - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { String line; line = p ? F("Blank: ") : F("Pulse: "); @@ -891,7 +954,7 @@ boolean displayRawToReadableB32Hex(String& outputStr, decode_results results) line += ((char)((bstMul - (uint16_t)bstMul) * 10) + '0'); addLogMove(LOG_LEVEL_DEBUG, line); } - #endif + # endif // ifndef BUILD_NO_DEBUG } // Generate the B32 Hex string, per the divisors found. diff --git a/src/_P035_IRTX.ino b/src/_P035_IRTX.ino index 52c66ef22..597eaf535 100644 --- a/src/_P035_IRTX.ino +++ b/src/_P035_IRTX.ino @@ -6,6 +6,7 @@ // ####################################################################################################### // // Changelog: +// 2022-08-08, tonhuisman: Fix listProtocols()/listACProtocols() to ignore 1-character type names // 2022-01-11, tonhuisman: Move all code and globals to PluginStructs/P035_data_struct to enable multi-instance use // No previous changelog recorded. @@ -75,7 +76,7 @@ boolean Plugin_035(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_WEBFORM_LOAD: { addRowLabel(F("Command")); - addHtml(F("IRSEND,[PROTOCOL],[DATA],[BITS optional],[REPEATS optional]
BITS and REPEATS are optional and default to 0")); + addHtml(F("IRSEND,[PROTOCOL],[DATA],[BITS optional],[REPEATS optional]
BITS and REPEATS are optional and default to 0
")); addHtml(F("IRSENDAC,{JSON formated AC command}")); success = true; diff --git a/src/_P095_ILI9341.ino b/src/_P095_ILI9341.ino index 28927742f..bf84f1f5a 100644 --- a/src/_P095_ILI9341.ino +++ b/src/_P095_ILI9341.ino @@ -7,18 +7,24 @@ # define PLUGIN_095 # define PLUGIN_ID_095 95 -# define PLUGIN_NAME_095 "Display - TFT 2.4 inches ILI9341" +# define PLUGIN_NAME_095 "Display - TFT ILI934x/ILI948x" # define PLUGIN_VALUENAME1_095 "CursorX" # define PLUGIN_VALUENAME2_095 "CursorY" # define PLUGIN_095_MAX_DISPLAY 1 -# if !defined(LIMIT_BUILD_SIZE) && !defined(PLUGIN_095_FONT_INCLUDED) - # define PLUGIN_095_FONT_INCLUDED // enable to use fonts in this plugin -# endif // if !defined(LIMIT_BUILD_SIZE) && !defined(PLUGIN_095_FONT_INCLUDED) - /** * Changelog: + * 2022-07-20 tonhuisman: Made support for ILI9486/ILI9488 optional and excluded by default as these are not available as + * regular SPI devices (3/4 wire SPI) + * NOTE: Renumbered enum with display types. + * 2022-07-16 tonhuisman: Add support for some more ILI9481 sub-types (Again cloned from TFT_eSPI library) + * WARNING: ILI9481 does *NOT* support changing rotation and keep writing on the display! + * Display memory is restructured by the rotation change, but the content is not adjusted. + * 2022-06-14 tonhuisman: Improved Splash handling, non-blocking delay, default 3 seconds + * 2022-06-11 tonhuisman: Implement support for getting config values, see AdafruitGFX_Helper.h changelog for details. Code optimization + * 2022-05-17 tonhuisman: Add setting for Splash during plugin startup, default on, when compiled in + * 2022-01-09 tonhuisman: Add support for ILI9342 (M5Stack, 240x320), ILI9481, ILI9486 and ILI9488 (320x480) displays * 2021-11-16 tonhuisman: Add support for PLUGIN_GET_DISPLAY_PARAMETERS, removed commented old source * 2021-08-17 tonhuisman: Reformatted source using Uncrustify, small cleanups * 2021-08-16 tonhuisman: Initial refactoring into the use of AdafruitGFX_helper @@ -211,72 +217,134 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_095)); } - AdaGFXFormBacklight(F("p095_backlight"), P095_CONFIG_BACKLIGHT_PIN, - F("p095_backpercentage"), P095_CONFIG_BACKLIGHT_PERCENT); + AdaGFXFormBacklight(F("pbacklight"), P095_CONFIG_BACKLIGHT_PIN, + F("pbkpercent"), P095_CONFIG_BACKLIGHT_PERCENT); - AdaGFXFormDisplayButton(F("p095_button"), P095_CONFIG_BUTTON_PIN, - F("p095_buttonInverse"), bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_INVERT_BUTTON), - F("p095_timer"), P095_CONFIG_DISPLAY_TIMEOUT); + AdaGFXFormDisplayButton(F("pbutton"), P095_CONFIG_BUTTON_PIN, + F("pbtnInverse"), bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_INVERT_BUTTON), + F("ptimer"), P095_CONFIG_DISPLAY_TIMEOUT); + + { + const __FlashStringHelper *hardwareTypes[] = { + ILI9xxx_type_toString(ILI9xxx_type_e::ILI9341_240x320), + ILI9xxx_type_toString(ILI9xxx_type_e::ILI9342_240x320), + ILI9xxx_type_toString(ILI9xxx_type_e::ILI9481_320x480), + ILI9xxx_type_toString(ILI9xxx_type_e::ILI9481_CPT29_320x480), + ILI9xxx_type_toString(ILI9xxx_type_e::ILI9481_PVI35_320x480), + ILI9xxx_type_toString(ILI9xxx_type_e::ILI9481_AUO317_320x480), + ILI9xxx_type_toString(ILI9xxx_type_e::ILI9481_CMO35_320x480), + ILI9xxx_type_toString(ILI9xxx_type_e::ILI9481_RGB_320x480), + ILI9xxx_type_toString(ILI9xxx_type_e::ILI9481_CMI7_320x480), + ILI9xxx_type_toString(ILI9xxx_type_e::ILI9481_CMI8_320x480), + # ifdef P095_ENABLE_ILI948X + ILI9xxx_type_toString(ILI9xxx_type_e::ILI9486_320x480), + ILI9xxx_type_toString(ILI9xxx_type_e::ILI9488_320x480), + # endif // ifdef P095_ENABLE_ILI948X + }; + const int hardwareOptions[] = { + static_cast(ILI9xxx_type_e::ILI9341_240x320), + static_cast(ILI9xxx_type_e::ILI9342_240x320), + static_cast(ILI9xxx_type_e::ILI9481_320x480), + static_cast(ILI9xxx_type_e::ILI9481_CPT29_320x480), + static_cast(ILI9xxx_type_e::ILI9481_PVI35_320x480), + static_cast(ILI9xxx_type_e::ILI9481_AUO317_320x480), + static_cast(ILI9xxx_type_e::ILI9481_CMO35_320x480), + static_cast(ILI9xxx_type_e::ILI9481_RGB_320x480), + static_cast(ILI9xxx_type_e::ILI9481_CMI7_320x480), + static_cast(ILI9xxx_type_e::ILI9481_CMI8_320x480), + # ifdef P095_ENABLE_ILI948X + static_cast(ILI9xxx_type_e::ILI9486_320x480), + static_cast(ILI9xxx_type_e::ILI9488_320x480), + # endif // ifdef P095_ENABLE_ILI948X + }; + addFormSelector(F("TFT display model"), + F("ptype"), + static_cast(ILI9xxx_type_e::ILI9xxx_MAX), + hardwareTypes, + hardwareOptions, + P095_CONFIG_FLAG_GET_TYPE); + } addFormSubHeader(F("Layout")); - AdaGFXFormRotation(F("p095_rotate"), P095_CONFIG_ROTATION); + AdaGFXFormRotation(F("protate"), P095_CONFIG_ROTATION); - AdaGFXFormTextPrintMode(F("p095_mode"), P095_CONFIG_FLAG_GET_MODE); + AdaGFXFormTextPrintMode(F("pmode"), P095_CONFIG_FLAG_GET_MODE); - AdaGFXFormFontScaling(F("p095_fontscale"), P095_CONFIG_FLAG_GET_FONTSCALE); + AdaGFXFormFontScaling(F("pfontscale"), P095_CONFIG_FLAG_GET_FONTSCALE); - addFormCheckBox(F("Clear display on exit"), F("p095_clearOnExit"), bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_CLEAR_ON_EXIT)); + # ifdef P095_SHOW_SPLASH + addFormCheckBox(F("Show splash on start"), F("psplash"), P095_CONFIG_FLAG_GET_SHOW_SPLASH); + # endif // ifdef P095_SHOW_SPLASH + + addFormCheckBox(F("Clear display on exit"), F("pclrOnExit"), bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_CLEAR_ON_EXIT)); { const __FlashStringHelper *commandTriggers[] = { // Be sure to use all options available in the enum (except MAX)! P095_CommandTrigger_toString(P095_CommandTrigger::tft), - P095_CommandTrigger_toString(P095_CommandTrigger::ili9341) + P095_CommandTrigger_toString(P095_CommandTrigger::ili9341), + P095_CommandTrigger_toString(P095_CommandTrigger::ili9342), + P095_CommandTrigger_toString(P095_CommandTrigger::ili9481), + # ifdef P095_ENABLE_ILI948X + P095_CommandTrigger_toString(P095_CommandTrigger::ili9486), + P095_CommandTrigger_toString(P095_CommandTrigger::ili9488), + # endif // ifdef P095_ENABLE_ILI948X }; const int commandTriggerOptions[] = { static_cast(P095_CommandTrigger::tft), - static_cast(P095_CommandTrigger::ili9341) + static_cast(P095_CommandTrigger::ili9341), + static_cast(P095_CommandTrigger::ili9342), + static_cast(P095_CommandTrigger::ili9481), + # ifdef P095_ENABLE_ILI948X + static_cast(P095_CommandTrigger::ili9486), + static_cast(P095_CommandTrigger::ili9488), + # endif // ifdef P095_ENABLE_ILI948X }; addFormSelector(F("Write Command trigger"), - F("p095_commandtrigger"), + F("pcmdtrigger"), static_cast(P095_CommandTrigger::MAX), commandTriggers, commandTriggerOptions, P095_CONFIG_FLAG_GET_CMD_TRIGGER); + # ifndef BUILD_NO_DEBUG addFormNote(F("Select the command that is used to handle commands for this display.")); + # endif // ifndef BUILD_NO_DEBUG } // Inverted state! - addFormCheckBox(F("Wake display on receiving text"), F("p095_NoDisplay"), !bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_NO_WAKE)); + addFormCheckBox(F("Wake display on receiving text"), F("pNoDisplay"), !bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_NO_WAKE)); + # ifndef BUILD_NO_DEBUG addFormNote(F("When checked, the display wakes up at receiving remote updates.")); + # endif // ifndef BUILD_NO_DEBUG - AdaGFXFormTextColRowMode(F("p095_colrow"), bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_USE_COL_ROW) == 1); + AdaGFXFormTextColRowMode(F("pcolrow"), bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_USE_COL_ROW) == 1); - AdaGFXFormOnePixelCompatibilityOption(F("p095_compat"), !bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_COMPAT_P095)); // Inverse + AdaGFXFormOnePixelCompatibilityOption(F("pcompat"), !bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_COMPAT_P095)); // Inverse - AdaGFXFormTextBackgroundFill(F("p095_backfill"), bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_BACK_FILL) == 0); // Inverse + AdaGFXFormTextBackgroundFill(F("pbackfill"), bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_BACK_FILL) == 0); // Inverse addFormSubHeader(F("Content")); if (P095_CONFIG_COLORS == 0) { // For migrating from older release task settings P095_CONFIG_COLORS = ADAGFX_WHITE | (ADAGFX_BLACK << 16); } - AdaGFXFormForeAndBackColors(F("p095_foregroundcolor"), + AdaGFXFormForeAndBackColors(F("pfgcolor"), P095_CONFIG_GET_COLOR_FOREGROUND, - F("p095_backgroundcolor"), + F("pbgcolor"), P095_CONFIG_GET_COLOR_BACKGROUND); - String strings[P095_Nlines]; - LoadCustomTaskSettings(event->TaskIndex, strings, P095_Nlines, 0); - - String line; // Default reserved length is plenty uint16_t remain = DAT_TASKS_CUSTOM_SIZE; + { + String strings[P095_Nlines]; + LoadCustomTaskSettings(event->TaskIndex, strings, P095_Nlines, 0); - for (uint8_t varNr = 0; varNr < P095_Nlines; varNr++) { - line = F("Line "); - line += (varNr + 1); - addFormTextBox(line, getPluginCustomArgName(varNr), strings[varNr], P095_Nchars); - remain -= (strings[varNr].length() + 1); + + for (uint8_t varNr = 0; varNr < P095_Nlines; varNr++) { + String line = F("Line "); + line += (varNr + 1); + addFormTextBox(line, getPluginCustomArgName(varNr), strings[varNr], P095_Nchars); + remain -= (strings[varNr].length() + 1); + } } String remainStr; remainStr.reserve(15); @@ -293,45 +361,46 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) P095_CONFIG_VERSION = 2; // mark config V2 as already saved (next time, will not convert 'invalid' values) // PIN(0)..(2) are already set - P095_CONFIG_ROTATION = getFormItemInt(F("p095_rotate")); - P095_CONFIG_BUTTON_PIN = getFormItemInt(F("p095_button")); - P095_CONFIG_DISPLAY_TIMEOUT = getFormItemInt(F("p095_timer")); - P095_CONFIG_BACKLIGHT_PIN = getFormItemInt(F("p095_backlight")); - P095_CONFIG_BACKLIGHT_PERCENT = getFormItemInt(F("p095_backpercentage")); + P095_CONFIG_ROTATION = getFormItemInt(F("protate")); + P095_CONFIG_BUTTON_PIN = getFormItemInt(F("pbutton")); + P095_CONFIG_DISPLAY_TIMEOUT = getFormItemInt(F("ptimer")); + P095_CONFIG_BACKLIGHT_PIN = getFormItemInt(F("pbacklight")); + P095_CONFIG_BACKLIGHT_PERCENT = getFormItemInt(F("pbkpercent")); - uint32_t lSettings = 0; - bitWrite(lSettings, P095_CONFIG_FLAG_NO_WAKE, !isFormItemChecked(F("p095_NoDisplay"))); // Bit 0 NoDisplayOnReceivingText, - // reverse logic, default=checked! - bitWrite(lSettings, P095_CONFIG_FLAG_INVERT_BUTTON, isFormItemChecked(F("p095_buttonInverse"))); // Bit 1 buttonInverse - bitWrite(lSettings, P095_CONFIG_FLAG_CLEAR_ON_EXIT, isFormItemChecked(F("p095_clearOnExit"))); // Bit 2 ClearOnExit - bitWrite(lSettings, P095_CONFIG_FLAG_USE_COL_ROW, isFormItemChecked(F("p095_colrow"))); // Bit 3 Col/Row addressing - bitWrite(lSettings, P095_CONFIG_FLAG_COMPAT_P095, !isFormItemChecked(F("p095_compat"))); // Bit 4 Compat_P095 (inv) - bitWrite(lSettings, P095_CONFIG_FLAG_BACK_FILL, !isFormItemChecked(F("p095_backfill"))); // Bit 5 Back fill text (inv) + bitWrite(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_NO_WAKE, !isFormItemChecked(F("pNoDisplay"))); // Bit 0 NoDisplayOnReceivingText, + // reverse logic, default=checked! + bitWrite(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_INVERT_BUTTON, isFormItemChecked(F("pbtnInverse"))); // Bit 1 buttonInverse + bitWrite(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_CLEAR_ON_EXIT, isFormItemChecked(F("pclrOnExit"))); // Bit 2 ClearOnExit + bitWrite(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_USE_COL_ROW, isFormItemChecked(F("pcolrow"))); // Bit 3 Col/Row addressing + bitWrite(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_COMPAT_P095, !isFormItemChecked(F("pcompat"))); // Bit 4 Compat_P095 (inv) + bitWrite(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_BACK_FILL, !isFormItemChecked(F("pbackfill"))); // Bit 5 Back fill text (inv) + # ifdef P095_SHOW_SPLASH + bitWrite(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_SHOW_SPLASH, !isFormItemChecked(F("psplash"))); // Bit 6 Splash on start (inv) + # endif // ifdef P095_SHOW_SPLASH - set4BitToUL(lSettings, P095_CONFIG_FLAG_CMD_TRIGGER, getFormItemInt(F("p095_commandtrigger"))); // Bit 8..11 Command trigger - set4BitToUL(lSettings, P095_CONFIG_FLAG_FONTSCALE, getFormItemInt(F("p095_fontscale"))); // Bit 12..15 Font scale - set4BitToUL(lSettings, P095_CONFIG_FLAG_MODE, getFormItemInt(F("p095_mode"))); // Bit 16..19 Text print mode - P095_CONFIG_FLAGS = lSettings; + set4BitToUL(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_CMD_TRIGGER, getFormItemInt(F("pcmdtrigger"))); // Bit 8..11 Command trigger + set4BitToUL(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_FONTSCALE, getFormItemInt(F("pfontscale"))); // Bit 12..15 Font scale + set4BitToUL(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_MODE, getFormItemInt(F("pmode"))); // Bit 16..19 Text print mode + set4BitToUL(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_TYPE, getFormItemInt(F("ptype"))); // Bit 20..24 Hardwaretype - String color = web_server.arg(F("p095_foregroundcolor")); - uint16_t fgcolor = ADAGFX_WHITE; // Default to white when empty + String color = web_server.arg(F("pfgcolor")); + uint16_t fgcolor = ADAGFX_WHITE; // Default to white when empty if (!color.isEmpty()) { - fgcolor = AdaGFXparseColor(color); // Reduce to rgb565 + fgcolor = AdaGFXparseColor(color); // Reduce to rgb565 } - color = web_server.arg(F("p095_backgroundcolor")); + color = web_server.arg(F("pbgcolor")); uint16_t bgcolor = AdaGFXparseColor(color); P095_CONFIG_COLORS = fgcolor | (bgcolor << 16); // Store as a single setting String strings[P095_Nlines]; - String error; for (uint8_t varNr = 0; varNr < P095_Nlines; varNr++) { strings[varNr] = web_server.arg(getPluginCustomArgName(varNr)); } - error = SaveCustomTaskSettings(event->TaskIndex, strings, P095_Nlines, 0); + String error = SaveCustomTaskSettings(event->TaskIndex, strings, P095_Nlines, 0); if (error.length() > 0) { addHtmlError(error); @@ -343,8 +412,11 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_GET_DISPLAY_PARAMETERS: { - event->Par1 = 240; // X-resolution in pixels - event->Par2 = 320; // Y-resolution in pixels + uint16_t _x, _y; + ILI9xxx_type_toResolution(static_cast(P095_CONFIG_FLAG_GET_TYPE), _x, _y); + + event->Par1 = _x; // X-resolution in pixels + event->Par2 = _y; // Y-resolution in pixels event->Par3 = P095_CONFIG_ROTATION; // Rotation (0..3: 0, 90, 180, 270 degrees) event->Par4 = static_cast(AdaGFXColorDepth::FullColor); // Color depth @@ -356,7 +428,8 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) { if (Settings.InitSPI != 0) { initPluginTaskData(event->TaskIndex, - new (std::nothrow) P095_data_struct(P095_CONFIG_ROTATION, + new (std::nothrow) P095_data_struct(static_cast(P095_CONFIG_FLAG_GET_TYPE), + P095_CONFIG_ROTATION, P095_CONFIG_FLAG_GET_FONTSCALE, static_cast(P095_CONFIG_FLAG_GET_MODE), P095_CONFIG_BACKLIGHT_PIN, @@ -394,12 +467,10 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) if (P095_CONFIG_BUTTON_PIN != -1) { P095_data_struct *P095_data = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr == P095_data) { - return success; + if (nullptr != P095_data) { + P095_data->registerButtonState(digitalRead(P095_CONFIG_BUTTON_PIN), bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_INVERT_BUTTON)); + success = true; } - - P095_data->registerButtonState(digitalRead(P095_CONFIG_BUTTON_PIN), bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_INVERT_BUTTON)); - success = true; } break; } @@ -445,7 +516,6 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) } } - return success; } diff --git a/src/_P129_74HC165.ino b/src/_P129_74HC165.ino new file mode 100644 index 000000000..6a474e85e --- /dev/null +++ b/src/_P129_74HC165.ino @@ -0,0 +1,459 @@ +#include "_Plugin_Helper.h" + +#ifdef USES_P129 + +// ####################################################################################################### +// ################################ Plugin 129 74HC165 Shiftregisters ############################### +// ####################################################################################################### + +/** Changelog: + * 2022-08-05 tonhuisman: Fix issue with reading 8th bit of each byte (found during HW testing) + * Reduce number of Values to match the selected number of chips/4. Small UI improvements. + * Enable pin is no longer required, as it is not available or required on some boards. + * 2022-07-30 tonhuisman: Remove Testing tag from plugin name. + * 2022-06-12 tonhuisman: Optimizations and small fixes. Implement use of PCONFIG_ULONG() + * 2022-02-25 tonhuisman: Rename command to ShiftIn,,... + * 2022-02-23 tonhuisman: Add command handling. + * 2022-02-22 tonhuisman: Compare results and generate events. + * 2022-02-21 tonhuisman: Add output selection dec + hex/bin, dec or hex/bin. + * 2022-02-20 tonhuisman: Initial plugin development. + * Based on a Forum request: https://www.letscontrolit.com/forum/viewtopic.php?p=57072&hilit=74hc165#p57072 + */ + +/** Commands: + * These commands only change configuration settings, but do not save them. They can be saved using the 'save' command. + * + * ShiftIn,PinEvent,,<0|1> : Set the event enable state for pin (1..128), max. up to configured chips * 8. + * ShiftIn,ChipEvent,,<0|1> : Set the event enable state for an entire chip (1..16), max. up to configured chips. + * ShiftIn,SetChipCount, : Set the number of chips, up to 16 (P129_MAX_CHIP_COUNT). + * ShiftIn,SampleFrequency,<0|1> : Set the sample frequency, 0 = 10x/sec, 1 = 50x/sec. + * ShiftIn,EventPerPin,<0|1> : Set events per pin off or on. + */ + +# define PLUGIN_129 +# define PLUGIN_ID_129 129 +# define PLUGIN_NAME_129 "Input - Shift registers (74HC165)" +# define PLUGIN_VALUENAME1_129 "State_A" +# define PLUGIN_VALUENAME2_129 "State_B" +# define PLUGIN_VALUENAME3_129 "State_C" +# define PLUGIN_VALUENAME4_129 "State_D" + +# include "./src/PluginStructs/P129_data_struct.h" + +// TODO tonhuisman: ? Move to StringConverter ? though it is a bit specific, can also be used by P126 +String P129_ul2stringFixed(uint32_t value, uint8_t base) { + uint64_t val = static_cast(value); + + val &= 0x0ffffffff; // Keep 32 bits + val |= 0x100000000; // Set bit just left of 32 bits so we will see the leading zeroes + String valStr = ull2String(val, base); + + valStr.remove(0, 1); // Delete leading 1 we added + + if (base == HEX) { + valStr.toUpperCase(); // uppercase hex for readability + } + return valStr; +} + +boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string) +{ + boolean success = false; + + switch (function) + { + case PLUGIN_DEVICE_ADD: + { + Device[++deviceCount].Number = PLUGIN_ID_129; + Device[deviceCount].Type = DEVICE_TYPE_TRIPLE; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_QUAD; + Device[deviceCount].Ports = 0; + Device[deviceCount].PullUpOption = false; + Device[deviceCount].InverseLogicOption = false; + Device[deviceCount].FormulaOption = false; + Device[deviceCount].DecimalsOnly = false; + Device[deviceCount].ValueCount = + # if P129_MAX_CHIP_COUNT <= 4 + 1 + # elif P129_MAX_CHIP_COUNT <= 8 + 2 + # elif P129_MAX_CHIP_COUNT <= 12 + 3 + # else // if P129_MAX_CHIP_COUNT > 12 + 4 + # endif // if P129_MAX_CHIP_COUNT <= 4 + ; + Device[deviceCount].SendDataOption = true; // No use in sending the Values to a controller + Device[deviceCount].TimerOption = true; // Used to update the Devices page + Device[deviceCount].TimerOptional = true; + + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_129); + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_129)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_129)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_129)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_129)); + break; + } + + case PLUGIN_SET_DEFAULTS: + { + P129_CONFIG_CHIP_COUNT = 1; // Minimum is 1 chip + P129_CONFIG_DATA_PIN = -1; + P129_CONFIG_CLOCK_PIN = -1; + P129_CONFIG_ENABLE_PIN = -1; + P129_CONFIG_LOAD_PIN = -1; + ExtraTaskSettings.TaskDeviceValueDecimals[0] = 0; // No decimals needed + ExtraTaskSettings.TaskDeviceValueDecimals[1] = 0; // No decimals needed + ExtraTaskSettings.TaskDeviceValueDecimals[2] = 0; // No decimals needed + ExtraTaskSettings.TaskDeviceValueDecimals[3] = 0; // No decimals needed + break; + } + + case PLUGIN_GET_DEVICEGPIONAMES: + { + event->String1 = formatGpioName_input(F("Data (Q7)")); + event->String2 = formatGpioName_output(F("Clock (CP)")); + event->String3 = formatGpioName_output(F("Enable (EN) (opt.)")); + break; + } + + case PLUGIN_GET_DEVICEVALUECOUNT: + { + event->Par1 = min(static_cast(VARS_PER_TASK), + static_cast(ceil(P129_CONFIG_CHIP_COUNT / 4.0))); + success = true; + break; + } + + case PLUGIN_GET_DEVICEVTYPE: + { + event->sensorType = static_cast( + min(static_cast(VARS_PER_TASK), + static_cast(ceil(P129_CONFIG_CHIP_COUNT / 4.0)))); + event->idx = 0; + success = true; + break; + } + + case PLUGIN_WEBFORM_LOAD: + { + addFormPinSelect(PinSelectPurpose::Generic_output, + formatGpioName_output(F("Load (PL)")), + F("load_pin"), + P129_CONFIG_LOAD_PIN); + # ifndef LIMIT_BUILD_SIZE + addFormNote(F("GPIO pins for Data, Clock and Load must be configured to correctly initialize the plugin.")); + # endif // ifndef LIMIT_BUILD_SIZE + + addFormSubHeader(F("Device configuration")); + + { + String chipCount[P129_MAX_CHIP_COUNT]; + int chipOption[P129_MAX_CHIP_COUNT]; + + for (uint8_t i = 0; i < P129_MAX_CHIP_COUNT; i++) { + chipCount[i] = String(i + 1); + chipOption[i] = i + 1; + } + addFormSelector(F("Number of chips (Q7 → DS)"), + F("chipcnt"), + P129_MAX_CHIP_COUNT, + chipCount, + chipOption, + P129_CONFIG_CHIP_COUNT, + true); + + String unit = F("Daisychained 1.."); + unit += P129_MAX_CHIP_COUNT; + addUnit(unit); + # ifndef LIMIT_BUILD_SIZE + addFormNote(F("Changing the number of chips will reload the page and update the Event configuration.")); + # endif // ifndef LIMIT_BUILD_SIZE + } + + const __FlashStringHelper *frequencyOptions[] = { + F("10/sec (100 msec)"), + F("50/sec (20 msec)") }; + const int frequencyValues[] = { P129_FREQUENCY_10, P129_FREQUENCY_50 }; + addFormSelector(F("Sample frequency"), F("frequency"), 2, frequencyOptions, frequencyValues, P129_CONFIG_FLAGS_GET_READ_FREQUENCY); + + addFormSubHeader(F("Display and output")); + + # ifdef P129_SHOW_VALUES + addFormCheckBox(F("Values display (Off=Hex/On=Bin)"), F("valuesdisplay"), P129_CONFIG_FLAGS_GET_VALUES_DISPLAY == 1); + # endif // ifdef P129_SHOW_VALUES + + const __FlashStringHelper *outputOptions[] = { + F("Decimal & hex/bin"), + F("Decimal only"), + F("Hex/bin only") }; + const int outputValues[] = { P129_OUTPUT_BOTH, P129_OUTPUT_DEC_ONLY, P129_OUTPUT_HEXBIN }; + addFormSelector(F("Output selection"), F("outputsel"), 3, outputOptions, outputValues, P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION); + + addFormCheckBox(F("Separate events per pin"), F("separate_events"), P129_CONFIG_FLAGS_GET_SEPARATE_EVENTS == 1); + + addFormSubHeader(F("Event configuration")); + + { + addRowLabel(F("Enable change-event for")); + html_table(EMPTY_STRING); // Sub-table + html_table_header(F("Chip # ")); + html_table_header(F("Port:")); + html_table_header(F("D7")); + html_table_header(F("D6")); + html_table_header(F("D5")); + html_table_header(F("D4")); + html_table_header(F("D3")); + html_table_header(F("D2")); + html_table_header(F("D1")); + html_table_header(F("D0")); + + uint64_t bits = 0; + uint8_t off = 0; + + for (uint8_t i = 0; i < P129_CONFIG_CHIP_COUNT; i++) { + if (i % 4 == 0) { + bits = PCONFIG_ULONG(i / 4) & 0x0ffffffff; + off = 0; + # ifndef P129_DEBUG_LOG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + String log = F("74HC165 Reading from: "); + log += (i / 4); + log += F(", bits: "); + log += P129_ul2stringFixed(bits, BIN); + addLog(LOG_LEVEL_INFO, log); + } + # endif // ifndef P129_DEBUG_LOG + } + html_TR(); + addHtml(F("")); + addHtmlInt(i + 1); + html_TD(); + + for (uint8_t j = 0; j < 8; j++) { + html_TD(); + # if FEATURE_TOOLTIPS + String toolTip = F("Chip "); + toolTip += (i + 1); + toolTip += F(" port D"); + toolTip += (7 - j); + toolTip += F(", pin "); + toolTip += i * 8 + (8 - j); + # endif // if FEATURE_TOOLTIPS + addCheckBox(getPluginCustomArgName((i * 8 + (7 - j)) + 1), bitRead(bits, off * 8 + (7 - j)) == 1 + # if FEATURE_TOOLTIPS + , false // = not Disabled + , toolTip + # endif // if FEATURE_TOOLTIPS + ); + } + off++; + } + html_end_table(); + } + + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + P129_CONFIG_LOAD_PIN = getFormItemInt(F("load_pin")); + P129_CONFIG_CHIP_COUNT = getFormItemInt(F("chipcnt")); + + uint32_t lSettings = 0u; + + # ifdef P129_SHOW_VALUES + + if (isFormItemChecked(F("valuesdisplay"))) { bitSet(lSettings, P129_FLAGS_VALUES_DISPLAY); } + + if (isFormItemChecked(F("separate_events"))) { bitSet(lSettings, P129_FLAGS_SEPARATE_EVENTS); } + # endif // ifdef P129_SHOW_VALUES + + if (getFormItemInt(F("frequency"))) { bitSet(lSettings, P129_FLAGS_READ_FREQUENCY); } + set4BitToUL(lSettings, P129_FLAGS_OUTPUT_SELECTION, getFormItemInt(F("outputsel"))); + + P129_CONFIG_FLAGS = lSettings & 0xFFFF; + + uint64_t bits = 0; + uint8_t off = 0; + + for (uint8_t i = 0; i < P129_CONFIG_CHIP_COUNT; i++) { + if (i % 4 == 0) { + bits = 0; + off = 0; + } + + for (uint8_t j = 0; j < 8; j++) { + bitWrite(bits, off * 8 + (7 - j), isFormItemChecked(getPluginCustomArgName((i * 8 + (7 - j)) + 1))); + } + PCONFIG_ULONG(i / 4) = bits; + + # ifndef P129_DEBUG_LOG + + if (loglevelActiveFor(LOG_LEVEL_INFO) && ((i % 4 == 3) || (i == P129_CONFIG_CHIP_COUNT))) { + String log = F("74HC165 Writing to: "); + log += (i / 4); + log += F(", offset: "); + log += (off * 8); + log += F(", bits: "); + log += P129_ul2stringFixed(bits, BIN); + addLog(LOG_LEVEL_INFO, log); + } + # endif // ifndef P129_DEBUG_LOG + off++; + } + success = true; + break; + } + + case PLUGIN_INIT: + { + initPluginTaskData(event->TaskIndex, new (std::nothrow) P129_data_struct(P129_CONFIG_DATA_PIN, + P129_CONFIG_CLOCK_PIN, + P129_CONFIG_ENABLE_PIN, + P129_CONFIG_LOAD_PIN, + P129_CONFIG_CHIP_COUNT)); + P129_data_struct *P129_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if ((nullptr != P129_data) && P129_data->isInitialized()) { + success = P129_data->plugin_init(event); + } + + if (!success) { + addLog(LOG_LEVEL_ERROR, F("74HC165: Initialization error!")); + # ifdef P129_DEBUG_LOG + } else { + addLog(LOG_LEVEL_INFO, F("74HC165: Initialized.")); + # endif // ifdef P129_DEBUG_LOG + } + + break; + } + + case PLUGIN_READ: + { + P129_data_struct *P129_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P129_data) { + success = P129_data->plugin_read(event); // Get state + } + + break; + } + + case PLUGIN_TEN_PER_SECOND: + case PLUGIN_FIFTY_PER_SECOND: + { + if (((function == PLUGIN_TEN_PER_SECOND) && (P129_CONFIG_FLAGS_GET_READ_FREQUENCY == P129_FREQUENCY_10)) || + ((function == PLUGIN_FIFTY_PER_SECOND) && (P129_CONFIG_FLAGS_GET_READ_FREQUENCY == P129_FREQUENCY_50))) { + P129_data_struct *P129_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P129_data) { + success = P129_data->plugin_readData(event); + } + } + + break; + } + case PLUGIN_FORMAT_USERVAR: + { + string.clear(); + + if ((P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION == P129_OUTPUT_BOTH) || + (P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION == P129_OUTPUT_DEC_ONLY)) { + string += String(UserVar.getUint32(event->TaskIndex, event->idx)); + } + + if (P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION == P129_OUTPUT_BOTH) { + string += ','; + } + + if ((P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION == P129_OUTPUT_BOTH) || + (P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION == P129_OUTPUT_HEXBIN)) { + string += '0'; + string += (P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? 'b' : 'x'); + string += P129_ul2stringFixed(UserVar.getUint32(event->TaskIndex, event->idx), + # ifdef P129_SHOW_VALUES + (P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN : + # endif // ifdef P129_SHOW_VALUES + HEX + # ifdef P129_SHOW_VALUES + ) + # endif // ifdef P129_SHOW_VALUES + ); + } + success = true; + break; + } + + # ifdef P129_SHOW_VALUES + case PLUGIN_WEBFORM_SHOW_VALUES: + { + String state, label; + state.reserve(40); + String abcd = F("ABCDEFGH"); // In case anyone dares to extend VARS_PER_TASK to 8... + const uint16_t endCheck = P129_CONFIG_CHIP_COUNT + 4; // 4(.0) = nr of bytes in an uint32_t. + const uint16_t maxVar = min(static_cast(VARS_PER_TASK), static_cast(ceil(P129_CONFIG_CHIP_COUNT / 4.0))); + uint8_t dotInsert; + uint8_t dotOffset; + + for (uint16_t varNr = 0; varNr < maxVar; varNr++) { + if (P129_CONFIG_FLAGS_GET_VALUES_DISPLAY) { + label = F("Bin"); + state = F("0b"); + dotInsert = 10; + dotOffset = 9; + } else { + label = F("Hex"); + state = F("0x"); + dotInsert = 4; + dotOffset = 3; + } + label += F(" State_"); + label += abcd.substring(varNr, varNr + 1); + label += ' '; + + label += min(255, P129_CONFIG_SHOW_OFFSET + (4 * varNr) + 4); // Limited to max 255 chips + label += '_'; + label += (P129_CONFIG_SHOW_OFFSET + (4 * varNr) + 1); // 4 = nr of bytes in an uint32_t. + + if ((P129_CONFIG_SHOW_OFFSET + (4 * varNr) + 4) <= endCheck) { // Only show if still in range + state += P129_ul2stringFixed(UserVar.getUint32(event->TaskIndex, varNr), P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN : HEX); + + for (uint8_t i = 0; i < 3; i++, dotInsert += dotOffset) { // Insert readability separators + state = state.substring(0, dotInsert) + '.' + state.substring(dotInsert); + } + pluginWebformShowValue(event->TaskIndex, VARS_PER_TASK + varNr, label, state, true); + } + } + success = true; // Don't show the default value data + break; + } + # endif // ifdef P129_SHOW_VALUES + case PLUGIN_WRITE: + { + P129_data_struct *P129_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P129_data) { + success = P129_data->plugin_write(event, string); + } + + break; + } + } + return success; +} + +#endif // ifdef USES_P129 diff --git a/src/_P131_NeoPixelMatrix.ino b/src/_P131_NeoPixelMatrix.ino new file mode 100644 index 000000000..636e6f0b8 --- /dev/null +++ b/src/_P131_NeoPixelMatrix.ino @@ -0,0 +1,444 @@ +#include "_Plugin_Helper.h" + +#ifdef USES_P131 + +// ####################################################################################################### +// ########################### Plugin 131 NeoPixel Matrix display ######################################## +// ####################################################################################################### + +/** Changelog: + * 2022-07-30 tonhuisman: Add commands to set scroll-options (settext, setscroll, setstep, setspeed, setempty, setright) + * Fix issue that on startup the display wasn't cleared (unit reset should turn off the display) + * Changed initial Text print mode to 'Truncate exceeding message' to enable Scroll mode + * Removed Testing tag from the plugin name. It has been tested and proven stable. + * 2022-07-15 tonhuisman: Implement horizontal scrolling (left/right, opt. empty start) for predefined content + * 2022-07-11 tonhuisman: Fix Clear on Exit issue + * 2022-06-13 tonhuisman: Improved Splash handling, non-blocking delay, default 3 seconds + * 2022-06-12 tonhuisman: Fix reading settings before plugin_ten_per_second() is executed + * Implement PCONFIG_ULONG(n) macro + * 2022-06-11 tonhuisman: Cleanup and optimize, make startup-splash configurable + * Removed old setting for Brightness, check settings if older version has been used! + * 2022-03-16 tonhuisman: Add textmode option, start scrolling implementation + * 2022-03-12 tonhuisman: Add Max. Brightness setting. Bugfixing. + * 2022-03-11 tonhuisman: Add Initial Brightness setting + * 2022-03-05 tonhuisman: Initial plugin development. + * Forum request: https://www.letscontrolit.com/forum/viewtopic.php?p=38095&hilit=matrix#p38095 + */ + +# define PLUGIN_131 +# define PLUGIN_ID_131 131 +# define PLUGIN_NAME_131 "Display - NeoPixel Matrix" + +# include "./src/PluginStructs/P131_data_struct.h" + +boolean Plugin_131(uint8_t function, struct EventStruct *event, String& string) +{ + boolean success = false; + + switch (function) + { + case PLUGIN_DEVICE_ADD: + { + Device[++deviceCount].Number = PLUGIN_ID_131; + Device[deviceCount].Type = DEVICE_TYPE_SINGLE; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_NONE; + Device[deviceCount].Ports = 0; + Device[deviceCount].PullUpOption = false; + Device[deviceCount].InverseLogicOption = false; + Device[deviceCount].FormulaOption = false; + Device[deviceCount].ValueCount = 0; + Device[deviceCount].SendDataOption = false; + Device[deviceCount].TimerOption = true; + Device[deviceCount].TimerOptional = true; + + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_131); + break; + } + + case PLUGIN_SET_DEFAULTS: + { + P131_CONFIG_MATRIX_WIDTH = 8; // Default matrix width + P131_CONFIG_MATRIX_HEIGHT = 8; // Default matrix height + P131_CONFIG_TILE_WIDTH = 1; // Default tile width + P131_CONFIG_TILE_HEIGHT = 1; // Default tile height + + set8BitToUL(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE, + NEO_MATRIX_TOP | NEO_MATRIX_LEFT | NEO_MATRIX_ROWS | NEO_MATRIX_PROGRESSIVE | + NEO_TILE_TOP | NEO_TILE_LEFT | NEO_TILE_ROWS | NEO_TILE_PROGRESSIVE); + + set4BitToUL(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_MODE, static_cast(AdaGFXTextPrintMode::TruncateExceedingMessage)); + + set8BitToUL(P131_CONFIG_FLAGS_B, P131_CONFIG_FLAG_B_BRIGHTNESS, 40); // Default brightness + set8BitToUL(P131_CONFIG_FLAGS_B, P131_CONFIG_FLAG_B_MAXBRIGHT, 255); // Default max brightness + + P131_CONFIG_COLORS = ADAGFX_WHITE | (ADAGFX_BLACK << 16); + + break; + } + + case PLUGIN_GET_DEVICEGPIONAMES: + { + event->String1 = formatGpioName_output(F("DIN")); + break; + } + + case PLUGIN_WEBFORM_LOAD: + { + const int optionValuesZeroOne[] { 0, 1 }; + + { + const __FlashStringHelper *stripOptions[] = { F("GRB"), F("GRBW") }; // Selection copied from P038 + addFormSelector(F("Strip Type"), F("striptype"), 2, stripOptions, optionValuesZeroOne, P131_CONFIG_FLAGS_GET_STRIP_TYPE); + } + + { + const __FlashStringHelper *optionsTop[] = { + F("Top/Left"), + F("Top/Right"), + F("Bottom/Left"), + F("Bottom/Right") }; + const int optionValuesTop[] { 0, 2, 1, 3 }; + + const __FlashStringHelper *optionsRowCol[] = { + F("Rows"), + F("Columns") }; + + const __FlashStringHelper *optionsProZig[] = { + F("Progressive"), + F("Zigzag") }; + + addFormSubHeader(F("Matrix")); + + addFormNumericBox(F("Matrix width"), F("matrixwidth"), + P131_CONFIG_MATRIX_WIDTH, 1, 100); + addFormNumericBox(F("Matrix height"), F("matrixheight"), + P131_CONFIG_MATRIX_HEIGHT, 1, 100); + + addFormSelector(F("Matrix start-pixel"), F("matrixstart"), 4, optionsTop, optionValuesTop, + get2BitFromUL(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_TOP)); + + addFormSelector(F("Matrix Rows/Columns mode"), F("matrixrowcol"), 2, optionsRowCol, optionValuesZeroOne, + bitRead(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_RC)); + + addFormSelector(F("Matrix flow direction"), F("matrixprozig"), 2, optionsProZig, optionValuesZeroOne, + bitRead(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_PZ)); + + addFormSubHeader(F("Multiple matrices: Tiles")); + + addFormNumericBox(F("Tile matrix width"), F("tilewidth"), + P131_CONFIG_TILE_WIDTH, 1, 32); + addFormNumericBox(F("Tile matrix height"), F("tileheight"), + P131_CONFIG_TILE_HEIGHT, 1, P131_Nlines); + + addFormSelector(F("Tile start-matrix"), F("tilestart"), 4, optionsTop, optionValuesTop, + get2BitFromUL(P131_CONFIG_FLAGS, P131_FLAGS_TILE_TYPE_TOP)); + + addFormSelector(F("Tile Rows/Columns mode"), F("tilerowcol"), 2, optionsRowCol, optionValuesZeroOne, + bitRead(P131_CONFIG_FLAGS, P131_FLAGS_TILE_TYPE_RC)); + + addFormSelector(F("Tile flow direction"), F("tileprozig"), 2, optionsProZig, optionValuesZeroOne, + bitRead(P131_CONFIG_FLAGS, P131_FLAGS_TILE_TYPE_PZ)); + } + + addFormSubHeader(F("Display")); + + AdaGFXFormRotation(F("rotate"), P131_CONFIG_FLAG_GET_ROTATION); + + AdaGFXFormTextPrintMode(F("tpmode"), P131_CONFIG_FLAG_GET_MODE); + + addFormNumericBox(F("Initial brightness"), F("brightness"), P131_CONFIG_FLAG_GET_BRIGHTNESS, 0, 255); + addUnit(F("0..255")); + addFormNumericBox(F("Maximum allowed brightness"), F("maxbright"), P131_CONFIG_FLAG_GET_MAXBRIGHT, 1, 255); + addUnit(F("1..255")); + + AdaGFXFormFontScaling(F("fontscale"), P131_CONFIG_FLAG_GET_FONTSCALE, 4); + + # ifdef P131_SHOW_SPLASH + addFormCheckBox(F("Show splash on start"), F("splash"), P131_CONFIG_FLAG_GET_SHOW_SPLASH); + # endif // ifdef P131_SHOW_SPLASH + + addFormCheckBox(F("Clear display on exit"), F("clearOnExit"), bitRead(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_CLEAR_ON_EXIT)); + + { + const __FlashStringHelper *commandTriggers[] = { // Be sure to use all options available in the enum (except MAX)! + P131_CommandTrigger_toString(P131_CommandTrigger::neomatrix), + P131_CommandTrigger_toString(P131_CommandTrigger::neo) + }; + const int commandTriggerOptions[] = { + static_cast(P131_CommandTrigger::neomatrix), + static_cast(P131_CommandTrigger::neo) + }; + addFormSelector(F("Write Command trigger"), + F("commandtrigger"), + static_cast(P131_CommandTrigger::MAX), + commandTriggers, + commandTriggerOptions, + P131_CONFIG_FLAG_GET_CMD_TRIGGER); + # ifndef LIMIT_BUILD_SIZE + addFormNote(F("Select the command that is used to handle commands for this display.")); + # endif // ifndef LIMIT_BUILD_SIZE + } + + addFormSubHeader(F("Content")); + + AdaGFXFormForeAndBackColors(F("fgcolor"), + P131_CONFIG_GET_COLOR_FOREGROUND, + F("bgcolor"), + P131_CONFIG_GET_COLOR_BACKGROUND); + + { + String strings[P131_Nlines]; + LoadCustomTaskSettings(event->TaskIndex, strings, P131_Nlines, 0); + + uint16_t remain = DAT_TASKS_CUSTOM_SIZE; + + addFormSubHeader(F("Lines")); + addRowLabel(F("Lines")); + + html_table(EMPTY_STRING); // Sub-table + html_table_header(F("Line # ")); + html_table_header(F("Text")); + html_table_header(F("Scroll")); + html_table_header(F("Empty start")); + html_table_header(F("Scroll right")); + html_table_header(F("Stepsize")); + html_table_header(F("Speed")); + + for (uint8_t varNr = 0; varNr < P131_CONFIG_TILE_HEIGHT; varNr++) { + html_TR_TD(); // All columns use max. width available + addHtml(F(" ")); + addHtmlInt(varNr + 1); + + html_TD(); // Text + addTextBox(getPluginCustomArgName(varNr), + parseStringKeepCase(strings[varNr], 1), + P131_Nchars, + false, + false, + EMPTY_STRING, + EMPTY_STRING); + + String opts = parseString(strings[varNr], 2); + uint32_t optBits = 0; + validUIntFromString(opts, optBits); + + html_TD(); // Scroll + addCheckBox(getPluginCustomArgName(varNr + 100), bitRead(optBits, P131_OPTBITS_SCROLL) == 1, false + # if FEATURE_TOOLTIPS + , F("Scroll text") + # endif // if FEATURE_TOOLTIPS + ); + html_TD(); // Start with empty display, inverted setting + addCheckBox(getPluginCustomArgName(varNr + 200), bitRead(optBits, P131_OPTBITS_STARTBLANK) == 0, false + # if FEATURE_TOOLTIPS + , F("Start and end scroll with empty display") + # endif // if FEATURE_TOOLTIPS + ); + html_TD(); // Scroll from right + addCheckBox(getPluginCustomArgName(varNr + 300), bitRead(optBits, P131_OPTBITS_RIGHTSCROLL) == 1, false + # if FEATURE_TOOLTIPS + , F("Scroll from left to right") + # endif // if FEATURE_TOOLTIPS + ); + html_TD(); // Pixels per step, offset with -1 + addNumericBox(getPluginCustomArgName(varNr + 400), get4BitFromUL(optBits, P131_OPTBITS_SCROLLSTEP) + 1, 1, P131_MAX_SCROLL_STEPS + # if FEATURE_TOOLTIPS + , EMPTY_STRING, F("Scroll 1..16 pixels / step") + # endif // if FEATURE_TOOLTIPS + ); + + opts = parseString(strings[varNr], 3); + int scrollSpeed = 0; + validIntFromString(opts, scrollSpeed); + + if (scrollSpeed == 0) { scrollSpeed = 10; } + html_TD(); // Speed 0.1 seconds per step + addNumericBox(getPluginCustomArgName(varNr + 500), scrollSpeed, 1, P131_MAX_SCROLL_SPEED + # if FEATURE_TOOLTIPS + , EMPTY_STRING, F("Scroll-speed in 0.1 seconds / step") + # endif // if FEATURE_TOOLTIPS + ); + + remain -= (strings[varNr].length() + 1); + + if ((P131_CONFIG_TILE_HEIGHT > 1) && (varNr == P131_CONFIG_TILE_HEIGHT - 1)) { + String remainStr; + remainStr.reserve(15); + remainStr = F("Remaining: "); + remainStr += remain; + html_TD(); + addUnit(remainStr); + } + } + html_end_table(); + } + + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + const uint8_t prevLines = P131_CONFIG_TILE_HEIGHT; + P131_CONFIG_MATRIX_WIDTH = getFormItemInt(F("matrixwidth")); + P131_CONFIG_MATRIX_HEIGHT = getFormItemInt(F("matrixheight")); + P131_CONFIG_TILE_WIDTH = getFormItemInt(F("tilewidth")); + P131_CONFIG_TILE_HEIGHT = getFormItemInt(F("tileheight")); + + // Bits are already in the correct order/configuration to be passed on to the constructor + // Matrix bits + set2BitToUL(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_TOP, getFormItemInt(F("matrixstart")) & 0x03); + bitWrite(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_RC, getFormItemInt(F("matrixrowcol"))); + bitWrite(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_PZ, getFormItemInt(F("matrixprozig"))); + + // Tile bits + set2BitToUL(P131_CONFIG_FLAGS, P131_FLAGS_TILE_TYPE_TOP, getFormItemInt(F("tilestart")) & 0x03); + bitWrite(P131_CONFIG_FLAGS, P131_FLAGS_TILE_TYPE_RC, getFormItemInt(F("tilerowcol"))); + bitWrite(P131_CONFIG_FLAGS, P131_FLAGS_TILE_TYPE_PZ, getFormItemInt(F("tileprozig"))); + + // Other settings + set4BitToUL(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_MODE, getFormItemInt(F("tpmode"))); + set4BitToUL(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_ROTATION, getFormItemInt(F("rotate"))); + set4BitToUL(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_FONTSCALE, getFormItemInt(F("fontscale"))); + set4BitToUL(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_CMD_TRIGGER, getFormItemInt(F("commandtrigger"))); + + bitWrite(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_CLEAR_ON_EXIT, isFormItemChecked(F("clearOnExit"))); + bitWrite(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_STRIP_TYPE, getFormItemInt(F("striptype")) == 1); + # ifdef P131_SHOW_SPLASH + bitWrite(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_SHOW_SPLASH, !isFormItemChecked(F("splash"))); + # endif // ifdef P131_SHOW_SPLASH + + + set8BitToUL(P131_CONFIG_FLAGS_B, P131_CONFIG_FLAG_B_BRIGHTNESS, getFormItemInt(F("brightness"))); + set8BitToUL(P131_CONFIG_FLAGS_B, P131_CONFIG_FLAG_B_MAXBRIGHT, getFormItemInt(F("maxbright"))); + + String color = web_server.arg(F("fgcolor")); + uint16_t fgcolor = ADAGFX_WHITE; // Default to white when empty + + if (!color.isEmpty()) { + fgcolor = AdaGFXparseColor(color); // Reduce to rgb565 + } + color = web_server.arg(F("bgcolor")); + const uint16_t bgcolor = AdaGFXparseColor(color); + + P131_CONFIG_COLORS = fgcolor | (bgcolor << 16); // Store as a single setting + + String strings[P131_Nlines]; + String error; + error.reserve(70); + + for (uint8_t varNr = 0; varNr < prevLines; varNr++) { + error.clear(); + error += wrapWithQuotes(web_server.arg(getPluginCustomArgName(varNr))); + error += ','; + uint32_t optBits = 0; + bitWrite(optBits, P131_OPTBITS_SCROLL, isFormItemChecked(getPluginCustomArgName(varNr + 100))); + bitWrite(optBits, P131_OPTBITS_STARTBLANK, !isFormItemChecked(getPluginCustomArgName(varNr + 200))); // Inverted + bitWrite(optBits, P131_OPTBITS_RIGHTSCROLL, isFormItemChecked(getPluginCustomArgName(varNr + 300))); + set4BitToUL(optBits, P131_OPTBITS_SCROLLSTEP, getFormItemIntCustomArgName(varNr + 400) - 1); // Offset -1 + error += optBits; + error += ','; + error += getFormItemIntCustomArgName(varNr + 500); + strings[varNr] = error; + } + error.clear(); + + error += SaveCustomTaskSettings(event->TaskIndex, strings, P131_Nlines, 0); + + if (error.length() > 0) { + addHtmlError(error); + } + + success = true; + break; + } + + case PLUGIN_INIT: + { + if (PIN(0) != -1) { + # ifndef BUILD_NO_DEBUG + String log = F("neomatrix: INIT, matrixType: 0b"); + log += String(P131_CONFIG_FLAGS_GET_MATRIX_TYPE, BIN); + addLogMove(LOG_LEVEL_INFO, log); + # endif // ifndef BUILD_NO_DEBUG + initPluginTaskData(event->TaskIndex, new (std::nothrow) P131_data_struct(P131_CONFIG_MATRIX_WIDTH, + P131_CONFIG_MATRIX_HEIGHT, + P131_CONFIG_TILE_WIDTH, + P131_CONFIG_TILE_HEIGHT, + PIN(0), + P131_CONFIG_FLAGS_GET_MATRIX_TYPE, + P131_CONFIG_FLAGS_GET_STRIP_TYPE + ? NEO_GRBW + NEO_KHZ800 + : NEO_GRB + NEO_KHZ800, + P131_CONFIG_FLAG_GET_ROTATION, + P131_CONFIG_FLAG_GET_FONTSCALE, + static_cast(P131_CONFIG_FLAG_GET_MODE), + P131_CommandTrigger_toString(static_cast( + P131_CONFIG_FLAG_GET_CMD_TRIGGER)), + P131_CONFIG_FLAG_GET_BRIGHTNESS, + P131_CONFIG_FLAG_GET_MAXBRIGHT, + P131_CONFIG_GET_COLOR_FOREGROUND, + P131_CONFIG_GET_COLOR_BACKGROUND)); + P131_data_struct *P131_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P131_data) { + success = P131_data->plugin_init(event); // Start the display + } + } else { + addLog(LOG_LEVEL_ERROR, F("NEOMATRIX: No GPIO pin configured, init cancelled.")); + } + + break; + } + + case PLUGIN_EXIT: + { + P131_data_struct *P131_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P131_data) { + success = P131_data->plugin_exit(event); + } + break; + } + + case PLUGIN_READ: + { + P131_data_struct *P131_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P131_data) { + success = P131_data->plugin_read(event); // Read operation, redisplay the configured content + } + break; + } + + case PLUGIN_WRITE: + { + P131_data_struct *P131_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P131_data) { + success = P131_data->plugin_write(event, string); // Write operation, handle commands, mostly delegated to AdafruitGFX_helper + } + + break; + } + + case PLUGIN_TEN_PER_SECOND: + { + P131_data_struct *P131_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P131_data) { + success = P131_data->plugin_ten_per_second(event); // 10 per second operation, update scrolling, handle splash counter + } + + break; + } + } + return success; +} + +#endif // ifdef USES_P131 diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index eac3cae24..606608ab8 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1291,6 +1291,7 @@ To create/register a plugin, you have to : #define USES_P121 // HMC5883L #define USES_P125 // ADXL345 SPI #define USES_P126 // 74HC595 Shift register + #define USES_P129 // 74HC165 Input shiftregisters #define USES_P133 // LTR390 UV #endif @@ -1344,6 +1345,9 @@ To create/register a plugin, you have to : #if !defined(LIMIT_BUILD_SIZE) && (defined(ESP8266) || !(ESP_IDF_VERSION_MAJOR > 3)) #define LIMIT_BUILD_SIZE // Reduce buildsize (on ESP8266 / pre-IDF4.x) to fit in all Display plugins #endif + #if !defined(FEATURE_SD) + #define FEATURE_SD 1 + #endif #ifndef USES_P012 #define USES_P012 // LCD #endif @@ -1396,6 +1400,9 @@ To create/register a plugin, you have to : #ifndef PLUGIN_DESCR #define PLUGIN_DESCR "NeoPixel" #endif + #if !defined(FEATURE_SD) && !defined(ESP8266) + #define FEATURE_SD 1 + #endif #ifndef USES_P038 #define USES_P038 // NeoPixel #endif @@ -1411,6 +1418,9 @@ To create/register a plugin, you have to : #ifndef USES_P128 #define USES_P128 // NeoPixelBusFX #endif + #ifndef USES_P131 + #define USES_P131 // NeoMatrix + #endif #if FEATURE_PLUGIN_STATS && defined(ESP8266) // Does not fit in build #undef FEATURE_PLUGIN_STATS @@ -1655,13 +1665,13 @@ To create/register a plugin, you have to : #define USES_P128 // NeoPixelBusFX #endif #ifndef USES_P129 -// #define USES_P129 // + #define USES_P129 // 74HC165 Input shiftregisters #endif #ifndef USES_P130 // #define USES_P130 // #endif #ifndef USES_P131 -// #define USES_P131 // + #define USES_P131 // NeoMatrix #endif #ifndef USES_P132 #define USES_P132 // INA3221 @@ -1733,7 +1743,7 @@ To create/register a plugin, you have to : #define DISABLE_SOFTWARE_SERIAL #endif -#if defined(USES_P095) || defined(USES_P096) || defined(USES_P116) +#if defined(USES_P095) || defined(USES_P096) || defined(USES_P116) || defined(USES_P131) // Add any plugin that uses AdafruitGFX_Helper #ifndef PLUGIN_USES_ADAFRUITGFX #define PLUGIN_USES_ADAFRUITGFX // Ensure AdafruitGFX_helper is available for graphics displays (only) #endif diff --git a/src/src/Helpers/AdafruitGFX_helper.cpp b/src/src/Helpers/AdafruitGFX_helper.cpp index 4e3aa3fc2..9274eca5d 100644 --- a/src/src/Helpers/AdafruitGFX_helper.cpp +++ b/src/src/Helpers/AdafruitGFX_helper.cpp @@ -48,6 +48,7 @@ const __FlashStringHelper* toString(AdaGFXTextPrintMode mode) { case AdaGFXTextPrintMode::ContinueToNextLine: return F("Continue to next line"); case AdaGFXTextPrintMode::TruncateExceedingMessage: return F("Truncate exceeding message"); case AdaGFXTextPrintMode::ClearThenTruncate: return F("Clear then truncate exceeding message"); + case AdaGFXTextPrintMode::TruncateExceedingCentered: return F("Truncate, centered if maxWidth set"); case AdaGFXTextPrintMode::MAX: break; } return F("None"); @@ -80,12 +81,14 @@ void AdaGFXFormTextPrintMode(const __FlashStringHelper *id, const __FlashStringHelper *textModes[textModeCount] = { // Be sure to use all available modes from enum! toString(AdaGFXTextPrintMode::ContinueToNextLine), toString(AdaGFXTextPrintMode::TruncateExceedingMessage), - toString(AdaGFXTextPrintMode::ClearThenTruncate) + toString(AdaGFXTextPrintMode::ClearThenTruncate), + toString(AdaGFXTextPrintMode::TruncateExceedingCentered), }; const int textModeOptions[textModeCount] = { static_cast(AdaGFXTextPrintMode::ContinueToNextLine), static_cast(AdaGFXTextPrintMode::TruncateExceedingMessage), - static_cast(AdaGFXTextPrintMode::ClearThenTruncate) + static_cast(AdaGFXTextPrintMode::ClearThenTruncate), + static_cast(AdaGFXTextPrintMode::TruncateExceedingCentered), }; addFormSelector(F("Text print Mode"), id, textModeCount, textModes, textModeOptions, selectedIndex); @@ -445,8 +448,10 @@ AdafruitGFX_helper::AdafruitGFX_helper(Adafruit_SPITFT *display, # endif // ifdef ADAGFX_ENABLE_BMP_DISPLAY -void AdafruitGFX_helper::initialize() -{ +/**************************************************************************** + * common initialization, called from constructors + ***************************************************************************/ +void AdafruitGFX_helper::initialize() { _trigger.toLowerCase(); // store trigger in lowercase # ifndef BUILD_NO_DEBUG @@ -595,7 +600,7 @@ bool AdafruitGFX_helper::processCommand(const String& string) { _display->setTextColor(_fgcolor, _bgcolor); } } - else if (subcommand.equals(F("txs")) && (argCount == 1)) + else if (subcommand.equals(F("txs")) && (argCount == 1)) // txs: Text size = font scaling, 1..10 { if ((nParams[0] >= 0) || (nParams[0] <= 10)) { _fontscaling = nParams[0]; @@ -605,7 +610,7 @@ bool AdafruitGFX_helper::processCommand(const String& string) { success = false; } } - else if (subcommand.equals(F("txtfull")) && (argCount >= 3) && (argCount <= 6)) { // txtfull: Text at position, with size and color + else if (subcommand.equals(F("txtfull")) && (argCount >= 3) && (argCount <= 8)) { // txtfull: Text at position, with size and color switch (argCount) { case 3: // single text @@ -676,6 +681,36 @@ bool AdafruitGFX_helper::processCommand(const String& string) { AdaGFXparseColor(sParams[4], _colorDepth)); } break; + case 7: // 7: text + size + color + bkcolor + printmode + case 8: // as 7 but: + maxwidth + + # if ADAGFX_ARGUMENT_VALIDATION + + if (invalidCoordinates(nParams[0] - _p095_compensation, nParams[1] - _p095_compensation, _columnRowMode)) { + success = false; + } else + # endif // if ADAGFX_ARGUMENT_VALIDATION + { + AdaGFXTextPrintMode tmpPrintMode = _textPrintMode; + + if ((nParams[5] >= 0) && (nParams[5] < static_cast(AdaGFXTextPrintMode::MAX))) { + _textPrintMode = static_cast(nParams[5]); + _display->setTextWrap(_textPrintMode == AdaGFXTextPrintMode::ContinueToNextLine); + } + printText(sParams[argCount - 1].c_str(), + nParams[0] - _p095_compensation, + nParams[1] - _p095_compensation, + nParams[2], + AdaGFXparseColor(sParams[3], _colorDepth), + AdaGFXparseColor(sParams[4], _colorDepth), + argCount == 8 ? nParams[argCount - 2] : 0); + + if (_textPrintMode != tmpPrintMode) { + _textPrintMode = tmpPrintMode; + _display->setTextWrap(_textPrintMode == AdaGFXTextPrintMode::ContinueToNextLine); + } + } + break; default: success = false; break; @@ -761,10 +796,10 @@ bool AdafruitGFX_helper::processCommand(const String& string) { if (sParams[0].equals(F("sevenseg24"))) { _display->setFont(&Seven_Segment24pt7b); - calculateTextMetrics(21, 42, 35); + calculateTextMetrics(21, 42, 35, true); } else if (sParams[0].equals(F("sevenseg18"))) { _display->setFont(&Seven_Segment18pt7b); - calculateTextMetrics(16, 32, 25); + calculateTextMetrics(16, 33, 26, true); } else if (sParams[0].equals(F("freesans"))) { _display->setFont(&FreeSans9pt7b); calculateTextMetrics(10, 16, 12); @@ -858,7 +893,7 @@ bool AdafruitGFX_helper::processCommand(const String& string) { # endif // ifdef ADAGFX_FONTS_EXTRA_20PT_INCLUDED } else if (sParams[0].equals(F("default"))) { // font,default is always available! _display->setFont(); - calculateTextMetrics(6, 10); + calculateTextMetrics(6, 9); } else { success = false; } @@ -1168,48 +1203,36 @@ bool AdafruitGFX_helper::processCommand(const String& string) { /**************************************************************************** * printText: Print text on display at a specific pixel or column/row location ***************************************************************************/ -void AdafruitGFX_helper::printText(const char *string, - int X, - int Y, - unsigned int textSize, - unsigned short color, - unsigned short bkcolor) { +void AdafruitGFX_helper::printText(const char *string, + int16_t X, + int16_t Y, + uint8_t textSize, + uint16_t color, + uint16_t bkcolor, + uint16_t maxWidth) { int16_t _x = X; int16_t _y = Y + (_heightOffset * textSize); uint16_t _w = 0; - uint8_t _h = 0; - int16_t x1, y1; - uint16_t w0, w1, h1; + int16_t x1 = 0, y1 = 0; + uint16_t w1 = 0, h1 = 0; + int16_t ot = 0, ob = 0, ol = 0; + uint16_t res_x = _res_x; + String newString = string; if (_columnRowMode) { _x = X * (_fontwidth * textSize); // We need this multiple times _y = (Y * (_fontheight * textSize)) + (_heightOffset * textSize); } - if (_textBackFill && (color != bkcolor)) { // Draw extra lines above text - // Estimate used width - _w = _textPrintMode == AdaGFXTextPrintMode::ContinueToNextLine ? - strlen(string) * _fontwidth * textSize : - ((_textcols + 1) * _fontwidth * textSize) - _x; - - do { - _display->drawLine(_x, _y, _x + _w - 1, _y, bkcolor); - _h++; - _y++; // Shift down entire line - } while (_h < textSize); - } - _display->setCursor(_x, _y); _display->setTextColor(color, bkcolor); _display->setTextSize(textSize); - String newString = string; - if (_textPrintMode != AdaGFXTextPrintMode::ContinueToNextLine) { if (_isProportional) { // Proportional font. This is rather slow! _display->getTextBounds(newString, _x, _y, &x1, &y1, &w1, &h1); // Count length - while ((newString.length() > 0) && ((_x + w1) > _res_x)) { + while ((newString.length() > 0) && ((_x + w1) > res_x)) { newString.remove(newString.length() - 1); // Cut last character off _display->getTextBounds(newString, _x, _y, &x1, &y1, &w1, &h1); // Re-count length } @@ -1221,43 +1244,77 @@ void AdafruitGFX_helper::printText(const char *string, } } - w1 = 0; + _display->getTextBounds(newString, _x, _y, &x1, &y1, &w1, &h1); // Count length - _display->getTextBounds(F(" "), _x, _y, &x1, &y1, &w1, &h1); + if ((maxWidth > 0) && (_x + maxWidth <= _res_x)) { + res_x = _x + maxWidth; + _w = maxWidth; - if (w1 == 0) { w1 = _fontwidth; } // Some fonts seem to have a 0-wide space, this is an endless loop protection - - if (_textPrintMode == AdaGFXTextPrintMode::ClearThenTruncate) { // Clear before print - _display->setCursor(_x, _y); - w0 = 0; - uint16_t w2 = 0; - - _display->getTextBounds(newString, _x, _y, &x1, &y1, &w2, &h1); // Count length in pixels - - for (; (w0 < w2); w0 += w1) { // Clear previously used text with spaces - _display->print(' '); + if ((_textPrintMode == AdaGFXTextPrintMode::TruncateExceedingCentered) && + (maxWidth > w1)) { + ol = (_w - (w1 + 2 * (x1 - _x))) / 2; } + } else { + _w = w1 + 2 * (x1 - _x); + } + + if (_textBackFill && (color != bkcolor)) { // Fill extra space above and below text + ot -= textSize; + ob += textSize; + _y += textSize; + } + + if ((_textPrintMode == AdaGFXTextPrintMode::ClearThenTruncate) || + (color != bkcolor)) { // Clear before print + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + String log = F("printText: clear: _x:"); + log += _x; + log += F(", _y:"); + log += _y; + log += F(", x1:"); + log += x1; + log += F(", y1:"); + log += y1; + log += F(", w1:"); + log += w1; + log += F(", h1:"); + log += h1; + log += F(", _res_x/max:"); + log += _res_x; + log += '/'; + log += res_x; + log += F(", str:"); + log += newString; + addLogMove(LOG_LEVEL_DEBUG, log); + } + # endif // ifndef BUILD_NO_DEBUG + + if (_textPrintMode == AdaGFXTextPrintMode::ClearThenTruncate) { + _display->fillRect(_x + ot, y1, res_x - _x, h1 + ob, bkcolor); // Clear text area to right edge of screen + } else { + _display->fillRect(_x + ot, y1, _w, h1 + ob, bkcolor); // Clear text area + } + delay(0); } - _display->setCursor(_x, _y); + _display->setCursor(_x + ol, _y); // add left offset to center, _y may be updated _display->print(newString); +} - _display->getTextBounds(newString, _x, _y, &x1, &y1, &w0, &h1); // Count length in pixels +/**************************************************************************** + * getTextSize length and height in pixels + ***************************************************************************/ +uint16_t AdafruitGFX_helper::getTextSize(const String& text, + uint16_t & h) { + int16_t x; + int16_t y; + uint16_t w; - for (; ((_x + w0) < _res_x) && _textPrintMode != AdaGFXTextPrintMode::ContinueToNextLine; w0 += w1) { - _display->print(' '); - } - - if (_textBackFill && (color != bkcolor)) { // Draw extra lines below text - _y += ((_fontheight - 1) * textSize); - _h = 0; - - do { - _display->drawLine(_x, _y - _h, _x + _w - 1, _y - _h, bkcolor); - _h++; - } while (_h <= textSize); - } + _display->getTextBounds(text.c_str(), 0, 0, &x, &y, &w, &h); // Count length and height in pixels + return w; } /**************************************************************************** @@ -1753,6 +1810,10 @@ bool AdafruitGFX_helper::invalidCoordinates(int X, # endif // if ADAGFX_ARGUMENT_VALIDATION +void AdafruitGFX_helper::setValidation(const bool& state) { + _useValidation = state; +} + void AdafruitGFX_helper::setRotation(uint8_t m) { uint8_t rotation = m & 3; diff --git a/src/src/Helpers/AdafruitGFX_helper.h b/src/src/Helpers/AdafruitGFX_helper.h index 34f93c337..ea90be9d4 100644 --- a/src/src/Helpers/AdafruitGFX_helper.h +++ b/src/src/Helpers/AdafruitGFX_helper.h @@ -160,11 +160,12 @@ enum class AdaGFX7Colors: uint16_t { # endif // if ADAGFX_SUPPORT_7COLOR enum class AdaGFXTextPrintMode : uint8_t { - ContinueToNextLine = 0u, - TruncateExceedingMessage = 1u, - ClearThenTruncate = 2u, // Should have max. 16 options + ContinueToNextLine = 0u, + TruncateExceedingMessage = 1u, + ClearThenTruncate = 2u, + TruncateExceedingCentered = 3u, // Should have max. 16 options - MAX // Keep as last + MAX // Keep as last }; # if ADAGFX_SUPPORT_7COLOR @@ -269,12 +270,13 @@ public: bool processCommand(const String& string); // Parse the string for recognized commands and apply them on the graphics display - void printText(const char *string, - int X, - int Y, - unsigned int textSize = 0, - unsigned short color = ADAGFX_WHITE, - unsigned short bkcolor = ADAGFX_BLACK); + void printText(const char *string, + int16_t X, + int16_t Y, + uint8_t textSize = 0, + uint16_t color = ADAGFX_WHITE, + uint16_t bkcolor = ADAGFX_BLACK, + uint16_t maxWidth = 0); void calculateTextMetrics(uint8_t fontwidth, uint8_t fontheight, int8_t heightOffset = 0, @@ -316,6 +318,14 @@ public: int16_t y); # endif // ifdef ADAGFX_ENABLE_BMP_DISPLAY + uint16_t getTextSize(const String& text, + uint16_t & h); // return length and height in pixels using current font + + void setValidation(const bool& state); + bool getValidation() const { + return _useValidation; + } + private: void initialize(); diff --git a/src/src/Helpers/I2C_Plugin_Helper.cpp b/src/src/Helpers/I2C_Plugin_Helper.cpp new file mode 100644 index 000000000..86b239374 --- /dev/null +++ b/src/src/Helpers/I2C_Plugin_Helper.cpp @@ -0,0 +1,25 @@ +#include "../Helpers/I2C_Plugin_Helper.h" + +/*********************************************************************** + * checkI2CConfigValid_toHtml(taskIndex, onlyCheck) + * Check if I2C is correctly configured and usable for this task + * taskIndex: will be used in planned enhancements + * outputToHtml = false: no html output is generated + * Outputs an error message and returns false if not correct + **********************************************************************/ +bool checkI2CConfigValid_toHtml(taskIndex_t taskIndex, + bool outputToHtml) { + if ((Settings.Pin_i2c_sda == -1) || (Settings.Pin_i2c_scl == -1)) { + if (outputToHtml) { addHtml(F("Incomplete I2C configuration.")); } + return false; + } + #if FEATURE_I2CMULTIPLEXER + + if ((Settings.I2C_Multiplexer_Type != I2C_MULTIPLEXER_NONE) && + (Settings.I2C_Multiplexer_Addr == -1)) { // Multiplexer selected, but no port configured + if (outputToHtml) { addHtml(F("Incomplete I2C Multiplexer configuration.")); } + return false; + } + #endif // if FEATURE_I2CMULTIPLEXER + return true; +} diff --git a/src/src/Helpers/I2C_Plugin_Helper.h b/src/src/Helpers/I2C_Plugin_Helper.h new file mode 100644 index 000000000..601e84c4b --- /dev/null +++ b/src/src/Helpers/I2C_Plugin_Helper.h @@ -0,0 +1,16 @@ +#ifndef _SRC_HELPERS_I2C_PLUGIN_HELPER_H +#define _SRC_HELPERS_I2C_PLUGIN_HELPER_H + +#include + +#include "../../ESPEasy_common.h" + +#include "../Globals/Plugins.h" +#include "../Globals/Settings.h" + +#include "../WebServer/HTML_wrappers.h" + +bool checkI2CConfigValid_toHtml(taskIndex_t taskIndex, + bool outputToHtml = true); + +#endif // ifndef _SRC_HELPERS_I2C_PLUGIN_HELPER_H diff --git a/src/src/PluginStructs/P016_data_struct.cpp b/src/src/PluginStructs/P016_data_struct.cpp index 9d3a96835..c730f1fe5 100644 --- a/src/src/PluginStructs/P016_data_struct.cpp +++ b/src/src/PluginStructs/P016_data_struct.cpp @@ -69,20 +69,23 @@ tCommandLinesV2::tCommandLinesV2(const tCommandLinesV1& lineV1, uint8_t i) P016_data_struct::P016_data_struct() {} void P016_data_struct::init(struct EventStruct *event, uint16_t CmdInhibitTime) { + # if P016_FEATURE_COMMAND_HANDLING loadCommandLines(event); iCmdInhibitTime = CmdInhibitTime; iLastCmd = 0; iLastCmdTime = 0; + # endif // if P016_FEATURE_COMMAND_HANDLING } +# if P016_FEATURE_COMMAND_HANDLING void P016_data_struct::loadCommandLines(struct EventStruct *event) { - # ifdef P16_SETTINGS_V1 + # ifdef P16_SETTINGS_V1 // Convert the settings if both versions are defined and PCONFIG(7) != latest version if (PCONFIG(7) != P16_SETTINGS_LATEST) { addLog(LOG_LEVEL_ERROR, F("P016 IR: Settings conversion, save task settings to store in new format.")); } - # endif // ifdef P16_SETTINGS_V1 + # endif // ifdef P16_SETTINGS_V1 CommandLines.clear(); // Start fresh for (uint8_t i = 0; i < P16_Nlines; i++) { @@ -99,13 +102,13 @@ void P016_data_struct::saveCommandLines(struct EventStruct *event) { void P016_data_struct::loadCommandLine(struct EventStruct *event, tCommandLinesV2& line, uint8_t lineNr) { - # ifdef P16_SETTINGS_V1 + # ifdef P16_SETTINGS_V1 if (PCONFIG(7) != P16_SETTINGS_LATEST) { loadCommandLinev1(event, line, lineNr); return; } - # endif // ifdef P16_SETTINGS_V1 + # endif // ifdef P16_SETTINGS_V1 const int loadOffset = lineNr * sizeof(tCommandLinesV2); LoadFromFile(SettingsType::Enum::CustomTaskSettings_Type, @@ -116,6 +119,8 @@ void P016_data_struct::loadCommandLine(struct EventStruct *event, tCommandLinesV line.Command[P16_Nchars - 1] = 0; // Terminate in case of uninitalized data } +# endif // if P016_FEATURE_COMMAND_HANDLING + # ifdef P16_SETTINGS_V1 void P016_data_struct::loadCommandLinev1(struct EventStruct *event, tCommandLinesV2& line, uint8_t lineNr) { @@ -129,12 +134,14 @@ void P016_data_struct::loadCommandLinev1(struct EventStruct *event, tCommandLine sizeof(tCommandLinesV2), loadOffsetV1); } + line = tCommandLinesV2(lineV1, lineNr); line.Command[P16_Nchars - 1] = 0; } # endif // ifdef P16_SETTINGS_V1 +# if P016_FEATURE_COMMAND_HANDLING void P016_data_struct::saveCommandLine(struct EventStruct *event, const tCommandLinesV2& line, uint8_t lineNr) { const int saveOffset = lineNr * sizeof(tCommandLinesV2); @@ -174,10 +181,11 @@ void P016_data_struct::AddCode(uint64_t Code, decode_type_t DecodeType, uint16_t CommandLines[_index].CodeDecodeType = DecodeType; CommandLines[_index].CodeFlags = CodeFlags; bCodeChanged = true; - # ifdef PLUGIN_016_DEBUG + # ifdef PLUGIN_016_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log; + if (log.reserve(80)) { // estimated log = F("[P36] AddCode: "); log += typeToString(DecodeType, bitRead(CodeFlags, P16_FLAGS_REPEAT)); @@ -188,7 +196,7 @@ void P016_data_struct::AddCode(uint64_t Code, decode_type_t DecodeType, uint16_t addLogMove(LOG_LEVEL_INFO, log); } } - # endif // PLUGIN_016_DEBUG + # endif // PLUGIN_016_DEBUG } void P016_data_struct::ExecuteCode(uint64_t Code, decode_type_t DecodeType, uint16_t CodeFlags) { @@ -213,14 +221,15 @@ void P016_data_struct::ExecuteCode(uint64_t Code, decode_type_t DecodeType, uint iLastCmdTime = millis(); if (CommandLines[i].Command[0] != 0) { - # ifdef PLUGIN_016_DEBUG + # ifdef PLUGIN_016_DEBUG bool _success = - # endif // ifdef PLUGIN_016_DEBUG + # endif // ifdef PLUGIN_016_DEBUG ExecuteCommand_all(EventValueSource::Enum::VALUE_SOURCE_SYSTEM, CommandLines[i].Command); - # ifdef PLUGIN_016_DEBUG + # ifdef PLUGIN_016_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log; + if (log.reserve(128)) { // estimated log = F("[P36] Execute: "); log += typeToString(DecodeType, bitRead(CodeFlags, P16_FLAGS_REPEAT)); @@ -238,14 +247,15 @@ void P016_data_struct::ExecuteCode(uint64_t Code, decode_type_t DecodeType, uint addLogMove(LOG_LEVEL_INFO, log); } } - # endif // PLUGIN_016_DEBUG + # endif // PLUGIN_016_DEBUG } return; } - # ifdef PLUGIN_016_DEBUG + # ifdef PLUGIN_016_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log; + if (log.reserve(128)) { // estimated log = F("[P36] ValidateCode failed: "); log += typeToString(DecodeType, bitRead(CodeFlags, P16_FLAGS_REPEAT)); @@ -261,7 +271,7 @@ void P016_data_struct::ExecuteCode(uint64_t Code, decode_type_t DecodeType, uint addLogMove(LOG_LEVEL_INFO, log); } } - # endif // PLUGIN_016_DEBUG + # endif // PLUGIN_016_DEBUG } } @@ -274,4 +284,6 @@ bool P016_data_struct::validateCode(int i, uint64_t Code, decode_type_t DecodeTy && (CommandLines[i].AlternativeCodeFlags == CodeFlags)); } +# endif // if P016_FEATURE_COMMAND_HANDLING + #endif // ifdef USES_P016 diff --git a/src/src/PluginStructs/P016_data_struct.h b/src/src/PluginStructs/P016_data_struct.h index e7e7ca033..43a92ebe0 100644 --- a/src/src/PluginStructs/P016_data_struct.h +++ b/src/src/PluginStructs/P016_data_struct.h @@ -6,9 +6,12 @@ # include -#include +# include -# define PLUGIN_016_DEBUG // additional debug messages in the log +# define PLUGIN_016_DEBUG // additional debug messages in the log +# if defined(LIMIT_BUILD_SIZE) && defined(PLUGIN_016_DEBUG) +# undef PLUGIN_016_DEBUG +# endif // if defined(LIMIT_BUILD_SIZE) && defined(PLUGIN_016_DEBUG) // bit definition in PCONFIG_LONG(0) # define P016_BitAddNewCode 0 // Add automatically new code into Code of the command structure @@ -19,6 +22,9 @@ # define P16_Nlines 10 // The number of different lines which can be displayed - each line is 64 chars max # define P16_Nchars 64 // max chars per command line # define P16_Cchars 20 // max chars per code +# define P016_DEFAULT_BUFFERSIZE 150 // default buffer size, range: 100..1024 +# define P016_MIN_BUFFERSIZE 100 // Minimum +# define P016_MAX_BUFFERSIZE 1024 // Maximum buffer size # define P16_SETTINGS_V1 // Settings v1 original settings when enabled, settings conversion is also enabled // Settings v2 includes 64 bit codes and some separated flags @@ -33,6 +39,14 @@ # define P16_FLAGS_REPEAT 0 // Repeat code // # define P16_FLAGS_HASH 1 // Code is a Hash +# ifndef P016_FEATURE_COMMAND_HANDLING +# define P016_FEATURE_COMMAND_HANDLING 1 // Enable Command handling and table +# endif // ifndef P016_FEATURE_COMMAND_HANDLING + +# if !P016_FEATURE_COMMAND_HANDLING && defined(P16_SETTINGS_V1) +# undef P16_SETTINGS_V1 +# endif // if !P016_FEATURE_COMMAND_HANDLING && defined(P16_SETTINGS_V1) + # ifdef P16_SETTINGS_V1 typedef struct { char Command[P16_Nchars] = { 0 }; @@ -41,12 +55,13 @@ typedef struct { } tCommandLinesV1; # endif // ifdef P16_SETTINGS_V1 +# if P016_FEATURE_COMMAND_HANDLING struct tCommandLinesV2 { - # ifdef P16_SETTINGS_V1 + # ifdef P16_SETTINGS_V1 tCommandLinesV2() = default; tCommandLinesV2(const tCommandLinesV1& lineV1, uint8_t i); - # endif // ifdef P16_SETTINGS_V1 + # endif // ifdef P16_SETTINGS_V1 char Command[P16_Nchars] = { 0 }; uint64_t Code = 0; // received code (can be added automatically) @@ -56,7 +71,7 @@ struct tCommandLinesV2 { uint16_t CodeFlags = 0; uint16_t AlternativeCodeFlags = 0; }; - +# endif // if P016_FEATURE_COMMAND_HANDLING struct P016_data_struct : public PluginTaskData_base { public: @@ -65,17 +80,18 @@ public: void init(struct EventStruct *event, uint16_t CmdInhibitTime); + # if P016_FEATURE_COMMAND_HANDLING void loadCommandLines(struct EventStruct *event); void saveCommandLines(struct EventStruct *event); static void loadCommandLine(struct EventStruct *event, tCommandLinesV2 & line, uint8_t lineNr); - # ifdef P16_SETTINGS_V1 + # ifdef P16_SETTINGS_V1 static void loadCommandLinev1(struct EventStruct *event, tCommandLinesV2 & line, uint8_t lineNr); - # endif // ifdef P16_SETTINGS_V1 + # endif // ifdef P16_SETTINGS_V1 static void saveCommandLine(struct EventStruct *event, const tCommandLinesV2& line, uint8_t lineNr); @@ -91,9 +107,11 @@ public: std::vectorCommandLines; // holds the CustomTaskSettings V2 bool bCodeChanged = false; // set if code has been added and CommandLines need to be saved (in PLUGIN_ONCE_A_SECOND) + # endif // if P016_FEATURE_COMMAND_HANDLING private: + # if P016_FEATURE_COMMAND_HANDLING bool validateCode(int i, uint64_t Code, decode_type_t DecodeType, @@ -104,6 +122,7 @@ private: decode_type_t iLastDecodeType = decode_type_t::UNKNOWN; // last decode_type sent uint16_t iCmdInhibitTime = 0; // inhibit time for sending the same command again uint16_t iLastCodeFlags = 0; // last flags sent + # endif // if P016_FEATURE_COMMAND_HANDLING }; #endif // ifdef USES_P016 diff --git a/src/src/PluginStructs/P035_data_struct.cpp b/src/src/PluginStructs/P035_data_struct.cpp index 90a0a35a9..50b993c05 100644 --- a/src/src/PluginStructs/P035_data_struct.cpp +++ b/src/src/PluginStructs/P035_data_struct.cpp @@ -44,12 +44,12 @@ bool P035_data_struct::plugin_init(struct EventStruct *event) { } } - if ((Plugin_035_irSender != nullptr) && (_gpioPin == -1)) { - addLog(LOG_LEVEL_INFO, F("INIT: IR TX Removed")); - delete Plugin_035_irSender; - Plugin_035_irSender = nullptr; - success = false; - } + // if ((Plugin_035_irSender != nullptr) && (_gpioPin == -1)) { // This can never be true because of the validGpio() check above + // addLog(LOG_LEVEL_INFO, F("INIT: IR TX Removed")); + // delete Plugin_035_irSender; + // Plugin_035_irSender = nullptr; + // success = false; + // } # ifdef P016_P035_Extended_AC @@ -63,12 +63,12 @@ bool P035_data_struct::plugin_init(struct EventStruct *event) { Plugin_035_commonAc = new (std::nothrow) IRac(_gpioPin); } - if ((Plugin_035_commonAc != nullptr) && (_gpioPin == -1)) { - addLog(LOG_LEVEL_INFO, F("INIT AC: IR TX Removed")); - delete Plugin_035_commonAc; - Plugin_035_commonAc = nullptr; - success = false; - } + // if ((Plugin_035_commonAc != nullptr) && (_gpioPin == -1)) { // This can never be true because of the validGpio() check above + // addLog(LOG_LEVEL_INFO, F("INIT AC: IR TX Removed")); + // delete Plugin_035_commonAc; + // Plugin_035_commonAc = nullptr; + // success = false; + // } # endif // ifdef P016_P035_Extended_AC return success; } @@ -425,6 +425,7 @@ void P035_data_struct::printToLog(const String& protocol, const String& data, in tmp += F(" Repeats: "); tmp += repeats; } + if (printToWeb) { printWebString = tmp; } @@ -435,9 +436,16 @@ void P035_data_struct::printToLog(const String& protocol, const String& data, in String P035_data_struct::listProtocols() { String temp; - for (uint32_t i = 0; i <= kLastDecodeType; i++) { - if (IRsend::defaultBits((decode_type_t)i) > 0) { - temp += typeToString((decode_type_t)i) + ' '; + if (temp.reserve(1024)) { + for (uint32_t i = 0; i <= kLastDecodeType; i++) { + if (IRsend::defaultBits((decode_type_t)i) > 0) { + String typ = typeToString((decode_type_t)i); + + if (typ.length() > 1) { + temp += typ; + temp += ' '; + } + } } } return temp; @@ -449,9 +457,16 @@ String P035_data_struct::listProtocols() { String P035_data_struct::listACProtocols() { String temp; - for (uint32_t i = 0; i <= kLastDecodeType; i++) { - if (hasACState((decode_type_t)i)) { - temp += typeToString((decode_type_t)i) + ' '; + if (temp.reserve(1024)) { + for (uint32_t i = 0; i <= kLastDecodeType; i++) { + if (hasACState((decode_type_t)i)) { + const String typ = typeToString((decode_type_t)i); + + if (typ.length() > 1) { + temp += typ; + temp += ' '; + } + } } } return temp; diff --git a/src/src/PluginStructs/P037_data_struct.cpp b/src/src/PluginStructs/P037_data_struct.cpp index e2b509311..ad5ae5a0c 100644 --- a/src/src/PluginStructs/P037_data_struct.cpp +++ b/src/src/PluginStructs/P037_data_struct.cpp @@ -205,20 +205,20 @@ bool P037_data_struct::webform_load( html_TR_TD(); addHtml(F(" ")); addHtmlInt(varNr + 1); - html_TD(); + html_TD(F("padding-right: 8px")); id = F("template"); id += (varNr + 1); addTextBox(id, mqttTopics[varNr], 40, - false, false, EMPTY_STRING, F("wide")); - html_TD(); + false, false, EMPTY_STRING, F("xwide")); + html_TD(F("padding-right: 8px")); id = F("attribute"); id += (varNr + 1); addTextBox(id, jsonAttributes[varNr], 20, - false, false, EMPTY_STRING, EMPTY_STRING); + false, false, EMPTY_STRING, F("xwide")); html_TD(); } else # endif // ifdef P037_JSON_SUPPORT diff --git a/src/src/PluginStructs/P095_data_struct.cpp b/src/src/PluginStructs/P095_data_struct.cpp index 991c82967..9308e1265 100644 --- a/src/src/PluginStructs/P095_data_struct.cpp +++ b/src/src/PluginStructs/P095_data_struct.cpp @@ -1,9 +1,66 @@ #include "../PluginStructs/P095_data_struct.h" #ifdef USES_P095 - # include "../Helpers/Hardware.h" +/**************************************************************************** + * ILI9xxx_type_toString: Display-value for the device selected + ***************************************************************************/ +const __FlashStringHelper* ILI9xxx_type_toString(ILI9xxx_type_e device) { + switch (device) { + case ILI9xxx_type_e::ILI9341_240x320: return F("ILI9341 240 x 320px"); + case ILI9xxx_type_e::ILI9342_240x320: return F("ILI9342 240 x 320px (M5Stack)"); + case ILI9xxx_type_e::ILI9481_320x480: return F("ILI9481 320 x 480px"); + case ILI9xxx_type_e::ILI9481_CPT29_320x480: return F("ILI9481 320 x 480px (CPT29)"); + case ILI9xxx_type_e::ILI9481_PVI35_320x480: return F("ILI9481 320 x 480px (PVI35)"); + case ILI9xxx_type_e::ILI9481_AUO317_320x480: return F("ILI9481 320 x 480px (AUO317)"); + case ILI9xxx_type_e::ILI9481_CMO35_320x480: return F("ILI9481 320 x 480px (CMO35)"); + case ILI9xxx_type_e::ILI9481_RGB_320x480: return F("ILI9481 320 x 480px (RGB)"); + case ILI9xxx_type_e::ILI9481_CMI7_320x480: return F("ILI9481 320 x 480px (CMI7)"); + case ILI9xxx_type_e::ILI9481_CMI8_320x480: return F("ILI9481 320 x 480px (CMI8)"); + # ifdef P095_ENABLE_ILI948X + case ILI9xxx_type_e::ILI9486_320x480: return F("ILI9486 320 x 480px"); + case ILI9xxx_type_e::ILI9488_320x480: return F("ILI9488 320 x 480px"); + # endif // ifdef P095_ENABLE_ILI948X + case ILI9xxx_type_e::ILI9xxx_MAX: break; + } + # ifndef BUILD_NO_DEBUG + return F("Unsupported type!"); + # else // ifndef BUILD_NO_DEBUG + return F(""); + # endif // ifndef BUILD_NO_DEBUG +} + +/**************************************************************************** + * ILI9xxx_type_toResolution: X and Y resolution for the selected type + ***************************************************************************/ +void ILI9xxx_type_toResolution(ILI9xxx_type_e device, uint16_t& x, uint16_t& y) { + switch (device) { + case ILI9xxx_type_e::ILI9341_240x320: + case ILI9xxx_type_e::ILI9342_240x320: + x = 240; + y = 320; + break; + case ILI9xxx_type_e::ILI9481_320x480: + case ILI9xxx_type_e::ILI9481_CPT29_320x480: + case ILI9xxx_type_e::ILI9481_PVI35_320x480: + case ILI9xxx_type_e::ILI9481_AUO317_320x480: + case ILI9xxx_type_e::ILI9481_CMO35_320x480: + case ILI9xxx_type_e::ILI9481_RGB_320x480: + case ILI9xxx_type_e::ILI9481_CMI7_320x480: + case ILI9xxx_type_e::ILI9481_CMI8_320x480: + # ifdef P095_ENABLE_ILI948X + case ILI9xxx_type_e::ILI9486_320x480: + case ILI9xxx_type_e::ILI9488_320x480: + # endif // ifdef P095_ENABLE_ILI948X + x = 320; + y = 480; + break; + case ILI9xxx_type_e::ILI9xxx_MAX: + break; + } +} + /**************************************************************************** * P095_CommandTrigger_toString: return the command string selected ***************************************************************************/ @@ -11,6 +68,12 @@ const __FlashStringHelper* P095_CommandTrigger_toString(P095_CommandTrigger cmd) switch (cmd) { case P095_CommandTrigger::tft: return F("tft"); case P095_CommandTrigger::ili9341: return F("ili9341"); + case P095_CommandTrigger::ili9342: return F("ili9342"); + case P095_CommandTrigger::ili9481: return F("ili9481"); + # ifdef P095_ENABLE_ILI948X + case P095_CommandTrigger::ili9486: return F("ili9486"); + case P095_CommandTrigger::ili9488: return F("ili9488"); + # endif // ifdef P095_ENABLE_ILI948X case P095_CommandTrigger::MAX: return F("None"); } return F("ili9341"); // Default command trigger @@ -19,7 +82,8 @@ const __FlashStringHelper* P095_CommandTrigger_toString(P095_CommandTrigger cmd) /**************************************************************************** * Constructor ***************************************************************************/ -P095_data_struct::P095_data_struct(uint8_t rotation, +P095_data_struct::P095_data_struct(ILI9xxx_type_e displayType, + uint8_t rotation, uint8_t fontscaling, AdaGFXTextPrintMode textmode, int8_t backlightPin, @@ -29,12 +93,14 @@ P095_data_struct::P095_data_struct(uint8_t rotation, uint16_t fgcolor, uint16_t bgcolor, bool textBackFill) - : _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _backlightPin(backlightPin), - _backlightPercentage(backlightPercentage), _displayTimer(displayTimer), _displayTimeout(displayTimer), - _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), _textBackFill(textBackFill) + : _displayType(displayType), _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), + _backlightPin(backlightPin), _backlightPercentage(backlightPercentage), _displayTimer(displayTimer), + _displayTimeout(displayTimer), _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), + _textBackFill(textBackFill) { _xpix = 240; _ypix = 320; + ILI9xxx_type_toResolution(_displayType, _xpix, _ypix); updateFontMetrics(); _commandTrigger.toLowerCase(); @@ -46,15 +112,8 @@ P095_data_struct::P095_data_struct(uint8_t rotation, * Destructor ***************************************************************************/ P095_data_struct::~P095_data_struct() { - if (nullptr != gfxHelper) { - delete gfxHelper; - gfxHelper = nullptr; - } - - if (nullptr != tft) { - delete tft; - tft = nullptr; - } + delete gfxHelper; + delete tft; } /**************************************************************************** @@ -64,9 +123,15 @@ bool P095_data_struct::plugin_init(struct EventStruct *event) { bool success = false; if (nullptr == tft) { + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("ILI9341: Init start.")); + # endif // ifndef BUILD_NO_DEBUG - tft = new (std::nothrow) Adafruit_ILI9341(PIN(0), PIN(1), PIN(2)); + tft = new (std::nothrow) Adafruit_ILI9341(PIN(0), PIN(1), PIN(2), static_cast(_displayType), _xpix, _ypix); + + if (nullptr != tft) { + tft->begin(); + } # ifndef BUILD_NO_DEBUG @@ -110,17 +175,24 @@ bool P095_data_struct::plugin_init(struct EventStruct *event) { gfxHelper->setTxtfullCompensation(!bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_COMPAT_P095) ? 0 : 1); } updateFontMetrics(); - tft->fillScreen(_bgcolor); // fill screen with black color - tft->setTextColor(_fgcolor, _bgcolor); // set text color to white and black background + tft->fillScreen(_bgcolor); // fill screen with background color + tft->setTextColor(_fgcolor, _bgcolor); // set text color to white and configured background tft->setTextSize(_fontscaling); // Handles 0 properly, text size, default 1 = very small tft->setCursor(0, 0); // move cursor to position (0, 0) pixel displayOnOff(true); # ifdef P095_SHOW_SPLASH - uint16_t yPos = 0; - gfxHelper->printText(String(F("ESPEasy")).c_str(), 0, yPos, 3, ST77XX_WHITE, ST77XX_BLUE); - yPos += (3 * _fontheight); - gfxHelper->printText(String(F("ILI9341")).c_str(), 0, yPos, 2, ST77XX_BLUE, ST77XX_WHITE); - delay(100); // Splash + + if (P095_CONFIG_FLAG_GET_SHOW_SPLASH) { + uint16_t yPos = 0; + gfxHelper->printText(String(F("ESPEasy")).c_str(), 0, yPos, 3, ADAGFX_WHITE, ADAGFX_BLUE); + yPos += (3 * _fontheight); + gfxHelper->printText(String(F("ILI934x/ILI948x")).c_str(), 0, yPos, 2, ADAGFX_BLUE, ADAGFX_WHITE); + _splashState = true; // Splash + _splashCounter = P095_SPLASH_DURATION; + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, F("P095 Splash start")); + # endif // ifndef BUILD_NO_DEBUG + } # endif // ifdef P095_SHOW_SPLASH updateFontMetrics(); @@ -150,20 +222,19 @@ void P095_data_struct::updateFontMetrics() { * plugin_exit: De-initialize before destruction ***************************************************************************/ bool P095_data_struct::plugin_exit(struct EventStruct *event) { + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, F("ILI9341: Exit.")); + # endif // ifndef BUILD_NO_DEBUG if ((nullptr != tft) && bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_CLEAR_ON_EXIT)) { tft->fillScreen(ADAGFX_BLACK); // fill screen with black color displayOnOff(false); } - if (nullptr != gfxHelper) { delete gfxHelper; } + delete gfxHelper; gfxHelper = nullptr; - if (nullptr != tft) { - // delete tft; // Library is not properly inherited so no destructor called - free(tft); // Free up some memory without calling the destructor chain - } + delete tft; tft = nullptr; return true; } @@ -172,7 +243,7 @@ bool P095_data_struct::plugin_exit(struct EventStruct *event) { * plugin_read: Re-draw the default content ***************************************************************************/ bool P095_data_struct::plugin_read(struct EventStruct *event) { - if (nullptr != tft) { + if ((nullptr != tft) && !_splashState) { String strings[P095_Nlines]; LoadCustomTaskSettings(event->TaskIndex, strings, P095_Nlines, 0); @@ -215,6 +286,27 @@ bool P095_data_struct::plugin_read(struct EventStruct *event) { * plugin_ten_per_second: check button, if any, that wakes up the display ***************************************************************************/ bool P095_data_struct::plugin_ten_per_second(struct EventStruct *event) { + # ifdef P095_SHOW_SPLASH + + if (_splashState) { // Decrement splash counter + _splashCounter--; + _splashState = _splashCounter != 0; + + if (!_splashState) { + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, F("P095 Splash finished.")); + # endif // ifndef BUILD_NO_DEBUG + + if (nullptr != tft) { + tft->fillScreen(_bgcolor); // fill screen with background color + } + + // Schedule the surrogate initial PLUGIN_READ that has been suppressed by the splash + Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + 10); + } + } + # endif // ifdef P095_SHOW_SPLASH + if ((P095_CONFIG_BUTTON_PIN != -1) && (getButtonState()) && (nullptr != tft)) { displayOnOff(true); markButtonStateProcessed(); @@ -226,7 +318,7 @@ bool P095_data_struct::plugin_ten_per_second(struct EventStruct *event) { * plugin_once_a_second: Count down display timer, if any, and turn display off if countdown reached ***************************************************************************/ bool P095_data_struct::plugin_once_a_second(struct EventStruct *event) { - if (_displayTimer > 0) { + if ((_displayTimer > 0) && !_splashState) { _displayTimer--; if ((nullptr != tft) && (_displayTimer == 0)) { @@ -243,7 +335,7 @@ bool P095_data_struct::plugin_write(struct EventStruct *event, const String& str bool success = false; String cmd = parseString(string, 1); - if ((nullptr != tft) && cmd.equals(_commandTriggerCmd)) { + if ((nullptr != tft) && cmd.equals(_commandTriggerCmd) && !_splashState) { String arg1 = parseString(string, 2); success = true; @@ -264,42 +356,30 @@ bool P095_data_struct::plugin_write(struct EventStruct *event, const String& str } } else if (arg1.equals(F("backlight"))) { - String arg2 = parseString(string, 3); - int nArg2; - - if ((P095_CONFIG_BACKLIGHT_PIN != -1) && // All is valid? - validIntFromString(arg2, nArg2) && - (nArg2 > 0) && - (nArg2 <= 100)) { - P095_CONFIG_BACKLIGHT_PERCENT = nArg2; // Set but don't store + if ((P095_CONFIG_BACKLIGHT_PIN != -1) && // All is valid? + (event->Par2 > 0) && + (event->Par2 <= 100)) { + P095_CONFIG_BACKLIGHT_PERCENT = event->Par2; // Set but don't store displayOnOff(true); } else { success = false; } } - else if (arg1.equals(F("inv"))) - { - String arg2 = parseString(string, 3); - int nArg2; - - if (validIntFromString(arg2, nArg2) && - (nArg2 >= 0) && - (nArg2 <= 1)) { - tft->invertDisplay(nArg2); + else if (arg1.equals(F("inv"))) { + if ((event->Par2 >= 0) && + (event->Par2 <= 1)) { + tft->invertDisplay(event->Par2); } else { success = false; } } - else if (arg1.equals(F("rot"))) - { - ///control?cmd=tftcmd,rot,0 - // not working to verify - String arg2 = parseString(string, 3); - int nArg2; - - if (validIntFromString(arg2, nArg2) && - (nArg2 >= 0)) { - tft->setRotation(nArg2 % 4); + else if (arg1.equals(F("rot"))) { + if ((event->Par2 >= 0)) { + if (nullptr != gfxHelper) { + gfxHelper->setRotation(event->Par2 % 4); + } else { + tft->setRotation(event->Par2 % 4); + } } else { success = false; } @@ -308,7 +388,7 @@ bool P095_data_struct::plugin_write(struct EventStruct *event, const String& str } } else if (tft && (cmd.equals(_commandTrigger) || - (gfxHelper && gfxHelper->isAdaGFXTrigger(cmd)))) { + (gfxHelper && gfxHelper->isAdaGFXTrigger(cmd))) && !_splashState) { success = true; if (!bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_NO_WAKE)) { // Wake display? @@ -347,11 +427,7 @@ void P095_data_struct::displayOnOff(bool state) { # endif // if defined(ESP32) } - if (state) { - tft->sendCommand(ILI9341_DISPON); - } else { - tft->sendCommand(ILI9341_DISPOFF); - } + tft->sendCommand(state ? ILI9341_DISPON : ILI9341_DISPOFF); _displayTimer = (state ? _displayTimeout : 0); } diff --git a/src/src/PluginStructs/P095_data_struct.h b/src/src/PluginStructs/P095_data_struct.h index 4d631ea6d..2a7347885 100644 --- a/src/src/PluginStructs/P095_data_struct.h +++ b/src/src/PluginStructs/P095_data_struct.h @@ -14,7 +14,13 @@ # define P095_Nchars 60 # define P095_DebounceTreshold 5 // number of 20 msec (fifty per second) ticks before the button has settled -// # define P095_SHOW_SPLASH // Enable to show initial splash (text) +// # define P095_ENABLE_ILI948X // Enable or disable support for ILI9486 and ILI9488. +// MUST reflect similar #define in Adafruit_ILI9341.h ! + +# ifndef LIMIT_BUILD_SIZE +# define P095_SHOW_SPLASH // Enable to show initial splash (text) +# endif // ifndef LIMIT_BUILD_SIZE +# define P095_SPLASH_DURATION (3000 / 100) // 3 seconds in 100 millisecond chunks # define P095_CONFIG_VERSION PCONFIG(0) // Settings version # define P095_CONFIG_ROTATION PCONFIG(1) // Rotation @@ -22,18 +28,20 @@ # define P095_CONFIG_DISPLAY_TIMEOUT PCONFIG(3) // Time-out when display-button is enable # define P095_CONFIG_BACKLIGHT_PIN PCONFIG(4) // Backlight pin # define P095_CONFIG_BACKLIGHT_PERCENT PCONFIG(5) // Backlight percentage -# define P095_CONFIG_COLORS PCONFIG_LONG(3) // 2 Colors fit in 1 long +# define P095_CONFIG_COLORS PCONFIG_ULONG(3) // 2 Colors fit in 1 long -# define P095_CONFIG_FLAGS PCONFIG_LONG(0) // All flags +# define P095_CONFIG_FLAGS PCONFIG_ULONG(0) // All flags # define P095_CONFIG_FLAG_NO_WAKE 0 // Flag: Don't wake display # define P095_CONFIG_FLAG_INVERT_BUTTON 1 // Flag: Inverted button state # define P095_CONFIG_FLAG_CLEAR_ON_EXIT 2 // Flag: Clear display on exit # define P095_CONFIG_FLAG_USE_COL_ROW 3 // Flag: Use Col/Row text addressing in commands # define P095_CONFIG_FLAG_COMPAT_P095 4 // Flag: Compatibility -1 offset like original P095 # define P095_CONFIG_FLAG_BACK_FILL 5 // Flag: Background fill when printing text +# define P095_CONFIG_FLAG_SHOW_SPLASH 6 // Flag: Show splash during startup of the plugin # define P095_CONFIG_FLAG_CMD_TRIGGER 8 // Flag-offset to store 4 bits for Command trigger, uses bits 8, 9, 10 and 11 # define P095_CONFIG_FLAG_FONTSCALE 12 // Flag-offset to store 4 bits for Font scaling, uses bits 12, 13, 14 and 15 # define P095_CONFIG_FLAG_MODE 16 // Flag-offset to store 4 bits for Mode, uses bits 16, 17, 18 and 19 +# define P095_CONFIG_FLAG_TYPE 20 // Flag-offset to store 4 bits for Display type, uses bits 20..24 // // Getters # define P095_CONFIG_GET_COLOR_FOREGROUND (P095_CONFIG_COLORS & 0xFFFF) @@ -41,6 +49,8 @@ # define P095_CONFIG_FLAG_GET_CMD_TRIGGER (get4BitFromUL(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_CMD_TRIGGER)) # define P095_CONFIG_FLAG_GET_FONTSCALE (get4BitFromUL(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_FONTSCALE)) # define P095_CONFIG_FLAG_GET_MODE (get4BitFromUL(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_MODE)) +# define P095_CONFIG_FLAG_GET_TYPE (get4BitFromUL(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_TYPE)) +# define P095_CONFIG_FLAG_GET_SHOW_SPLASH (!bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_SHOW_SPLASH)) // Inverted setting, default on # ifdef ESP32 @@ -53,24 +63,55 @@ # else // ifdef ESP32 // Was: for D1 Mini with shield connection - # define P095_TFT_CS 0 // D3 - # define P095_TFT_DC 4 // D2 + # define P095_TFT_CS 0 // D3 + # define P095_TFT_DC 4 // D2 # define P095_TFT_RST -1 // D4 // -1 # define P095_BACKLIGHT_PIN -1 // D6 // 15 // D8 -> Blocks Wemos # endif // ifdef ESP32 +enum class ILI9xxx_type_e : uint8_t { + ILI9341_240x320 = 0u, + ILI9342_240x320 = 1u, + ILI9481_320x480 = 2u, + ILI9481_CPT29_320x480 = 3u, + ILI9481_PVI35_320x480 = 4u, + ILI9481_AUO317_320x480 = 5u, + ILI9481_CMO35_320x480 = 6u, + ILI9481_RGB_320x480 = 7u, + ILI9481_CMI7_320x480 = 8u, + ILI9481_CMI8_320x480 = 9u, + # ifdef P095_ENABLE_ILI948X + ILI9486_320x480 = 10u, + ILI9488_320x480 = 11u, + ILI9xxx_MAX = 12u // last value = count + # else // ifdef P095_ENABLE_ILI948X + ILI9xxx_MAX = 10u // last value = count + # endif // ifdef P095_ENABLE_ILI948X +}; + enum class P095_CommandTrigger : uint8_t { tft = 0u, ili9341, + ili9342, + ili9481, + # ifdef P095_ENABLE_ILI948X + ili9486, + ili9488, + # endif // ifdef P095_ENABLE_ILI948X MAX // Keep as last item! }; +const __FlashStringHelper* ILI9xxx_type_toString(ILI9xxx_type_e device); const __FlashStringHelper* P095_CommandTrigger_toString(P095_CommandTrigger cmd); +void ILI9xxx_type_toResolution(ILI9xxx_type_e device, + uint16_t & x, + uint16_t & y); struct P095_data_struct : public PluginTaskData_base { public: - P095_data_struct(uint8_t rotation, + P095_data_struct(ILI9xxx_type_e displayType, + uint8_t rotation, uint8_t fontscaling, AdaGFXTextPrintMode textmode, int8_t backlightPin, @@ -81,7 +122,7 @@ public: uint16_t bgcolor = ADAGFX_BLACK, bool textBackFill = true); ~P095_data_struct(); - + bool plugin_init(struct EventStruct *event); bool plugin_exit(struct EventStruct *event); bool plugin_read(struct EventStruct *event); @@ -106,25 +147,26 @@ private: AdafruitGFX_helper *gfxHelper = nullptr; - uint16_t _xpix; - uint16_t _ypix; - uint16_t _textcols; - uint16_t _textrows; - uint8_t _fontwidth = 6; // Default font characteristics - uint8_t _fontheight = 10; - uint8_t _heightOffset = 0; + ILI9xxx_type_e _displayType = ILI9xxx_type_e::ILI9341_240x320; + uint16_t _xpix = 240; + uint16_t _ypix = 320; + uint16_t _textcols = 0; + uint16_t _textrows = 0; + uint8_t _fontwidth = 6; // Default font characteristics + uint8_t _fontheight = 10; + uint8_t _heightOffset = 0; - uint8_t _rotation; - uint8_t _fontscaling; - AdaGFXTextPrintMode _textmode; - int8_t _backlightPin; - uint8_t _backlightPercentage; - uint32_t _displayTimer; - uint32_t _displayTimeout; + uint8_t _rotation = 0; + uint8_t _fontscaling = 1; + AdaGFXTextPrintMode _textmode = AdaGFXTextPrintMode::ClearThenTruncate; + int8_t _backlightPin = -1; + uint8_t _backlightPercentage = 100; + uint32_t _displayTimer = 0; + uint32_t _displayTimeout = 0; String _commandTrigger; - uint16_t _fgcolor; - uint16_t _bgcolor; - bool _textBackFill; + uint16_t _fgcolor = ADAGFX_WHITE; + uint16_t _bgcolor = ADAGFX_BLACK; + bool _textBackFill = false; String _commandTriggerCmd; @@ -135,6 +177,11 @@ private: int8_t _leftMarginCompensation = 0; // Not settable yet int8_t _topMarginCompensation = 0; + + bool _splashState = false; // Have this always available to avoid 'many' #ifdefs in the code + # ifdef P095_SHOW_SPLASH + uint8_t _splashCounter = P095_SPLASH_DURATION; + # endif // ifdef P095_SHOW_SPLASH }; diff --git a/src/src/PluginStructs/P129_data_struct.cpp b/src/src/PluginStructs/P129_data_struct.cpp new file mode 100644 index 000000000..d6f650cd9 --- /dev/null +++ b/src/src/PluginStructs/P129_data_struct.cpp @@ -0,0 +1,242 @@ +#include "../PluginStructs/P129_data_struct.h" + +#ifdef USES_P129 + +// **************************************************************************/ +// Constructor +// **************************************************************************/ +P129_data_struct::P129_data_struct(int8_t dataPin, + int8_t clockPin, + int8_t enablePin, + int8_t loadPin, + uint8_t chipCount) + : _dataPin(dataPin), _clockPin(clockPin), _enablePin(enablePin), _loadPin(loadPin), _chipCount(chipCount) {} + +// **************************************************************************/ +// Destructor +// **************************************************************************/ +P129_data_struct::~P129_data_struct() {} + +bool P129_data_struct::plugin_init(struct EventStruct *event) { + if (isInitialized()) { + for (uint8_t i = 0; i < P129_MAX_CHIP_COUNT; i++) { // Clear entire buffer + readBuffer[i] = 0; + } + + // Prepare all used GPIO pins + if (validGpio(_enablePin)) { pinMode(_enablePin, OUTPUT); } + pinMode(_loadPin, OUTPUT); + pinMode(_clockPin, OUTPUT); + pinMode(_dataPin, INPUT); + digitalWrite(_loadPin, HIGH); + + if (validGpio(_enablePin)) { digitalWrite(_enablePin, HIGH); } + + return true; + } + return false; +} + +uint32_t P129_data_struct::getChannelState(uint8_t offset, + uint8_t size) const { + uint32_t result = 0u; + uint16_t sft = 0u; + const uint8_t last = offset + size; + + for (uint8_t ofs = offset; ofs < last; ofs++, sft++) { + result += (readBuffer[ofs] << (8 * sft)); + } + + # ifdef P129_DEBUG_LOG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + String log = F("SHIFTIN: getChannelState offset: "); + log += offset; + log += F(", size: "); + log += size; + log += F(", result: "); + log += result; + log += F("/0x"); + log += String(result, HEX); + addLogMove(LOG_LEVEL_DEBUG, log); + } + # endif // ifdef P129_DEBUG_LOG + + return result; +} + +bool P129_data_struct::plugin_read(struct EventStruct *event) { + const uint16_t last = P129_CONFIG_SHOW_OFFSET + (VARS_PER_TASK * 4); + uint8_t varNr = 0; + + for (uint16_t index = P129_CONFIG_SHOW_OFFSET; index < _chipCount && index < last && varNr < VARS_PER_TASK; index += 4, varNr++) { + uint32_t result = getChannelState(index, min(VARS_PER_TASK, _chipCount - index)); + UserVar.setUint32(event->TaskIndex, varNr, result); + } + return true; +} + +bool P129_data_struct::plugin_write(struct EventStruct *event, + const String & string) { + bool success = false; + + const String command = parseString(string, 1); + + if (command.equals(F("shiftin"))) { + const String subcommand = parseString(string, 2); + + if (subcommand.equals(F("pinevent"))) { // ShiftIn,pinevent,,<0|1> + const uint8_t pin = event->Par2 - 1; + const uint8_t value = event->Par3; + + if (validChannel(pin + 1) && ((value == 0) || (value == 1))) { + const uint8_t ulong = pin / 32; + const uint8_t bit = pin % 32; + uint32_t lSettings = PCONFIG_ULONG(ulong); + + bitWrite(lSettings, bit, value); + PCONFIG_ULONG(ulong) = lSettings; + success = true; + # ifdef P129_DEBUG_LOG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + String log = command; + log += F(", pin: "); + log += event->Par2; + log += F(", value: "); + log += value; + log += F(", config: "); + log += ulong; + log += F(", bit: "); + log += bit; + addLogMove(LOG_LEVEL_DEBUG, log); + } + # endif // ifdef P129_DEBUG_LOG + } + } else if (subcommand.equals(F("chipevent"))) { // ShiftIn,chipevent,,<0|1> + const uint8_t chip = event->Par2 - 1; + const uint8_t value = event->Par3; + + if ((chip >= 0) && (chip < P129_CONFIG_CHIP_COUNT) && ((value == 0) || (value == 1))) { + const uint8_t ulong = chip / 4; + const uint8_t bit = (chip % 4) * 8; + uint32_t lSettings = PCONFIG_ULONG(ulong); + + set8BitToUL(lSettings, bit, value == 1 ? 0xFF : 0x00); + PCONFIG_ULONG(ulong) = lSettings; + success = true; + # ifdef P129_DEBUG_LOG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + String log = command; + log += F(", chip: "); + log += event->Par2; + log += F(", value: "); + log += value; + log += F(", config: "); + log += ulong; + log += F(", bit: "); + log += bit; + addLogMove(LOG_LEVEL_DEBUG, log); + } + # endif // ifdef P129_DEBUG_LOG + } + } else if (subcommand.equals(F("setchipcount"))) { // ShiftIn,setchipcount, + if ((event->Par2 >= 1) && (event->Par2 <= P129_MAX_CHIP_COUNT)) { + P129_CONFIG_CHIP_COUNT = event->Par2; + _chipCount = event->Par2; + success = true; + } + } else if (subcommand.equals(F("samplefrequency"))) { // ShiftIn,samplefrequency,<0|1> + if ((event->Par2 == 0) || (event->Par2 == 1)) { + uint32_t lSettings = P129_CONFIG_FLAGS; + bitWrite(lSettings, P129_FLAGS_READ_FREQUENCY, event->Par2 == 1); + P129_CONFIG_FLAGS = lSettings; + success = true; + } + } else if (subcommand.equals(F("eventperpin"))) { // ShiftIn,eventperpin,<0|1> + if ((event->Par2 == 0) || (event->Par2 == 1)) { + uint32_t lSettings = P129_CONFIG_FLAGS; + bitWrite(lSettings, P129_FLAGS_SEPARATE_EVENTS, event->Par2 == 1); + P129_CONFIG_FLAGS = lSettings; + success = true; + } + } + # ifdef P129_DEBUG_LOG + + if (success) { + addLog(LOG_LEVEL_DEBUG, string); + } + # endif // ifdef P129_DEBUG_LOG + } + return success; +} + +bool P129_data_struct::plugin_readData(struct EventStruct *event) { + if (isInitialized()) { + digitalWrite(_loadPin, LOW); + delayMicroseconds(5); + digitalWrite(_loadPin, HIGH); + delayMicroseconds(5); + + if (validGpio(_enablePin)) { digitalWrite(_enablePin, LOW); } + + for (uint8_t i = 0; i < P129_CONFIG_CHIP_COUNT; i++) { + prevBuffer[i] = readBuffer[i]; + readBuffer[i] = shiftIn(static_cast(_dataPin), static_cast(_clockPin), MSBFIRST); + } + + if (validGpio(_enablePin)) { digitalWrite(_enablePin, HIGH); } + delay(0); + + checkDiff(event); + + return true; + } + return false; +} + +void P129_data_struct::checkDiff(struct EventStruct *event) { + for (uint8_t i = 0; i < P129_CONFIG_CHIP_COUNT; i += 4) { + if (PCONFIG_ULONG(i / 4) != 0) { // Any input event enabled? + const uint32_t read = readBuffer[i + 3] << 24 | readBuffer[i + 2] << 16 | readBuffer[i + 1] << 8 | readBuffer[i + 0]; + const uint32_t prev = prevBuffer[i + 3] << 24 | prevBuffer[i + 2] << 16 | prevBuffer[i + 1] << 8 | prevBuffer[i + 0]; + + for (uint8_t j = 0; j < 32; j++) { // Check all 32 bits + if (bitRead(PCONFIG_ULONG(i / 4), j) && (bitRead(read, j) != bitRead(prev, j))) { // Event enabled and bit changed? + sendInputEvent(event, i, j, bitRead(read, j)); // Send out new state + } + } + } + delay(0); + } +} + +void P129_data_struct::sendInputEvent(struct EventStruct *event, + uint8_t group, + uint8_t bit, + uint8_t state) { + String send; + const uint8_t pin = (group * 8) + bit + 1; // 1..128 + const uint8_t chip = group + (bit / 8) + 1; // 1..16 + const uint8_t port = (bit % 8); // 0..7 + + send.reserve(40); + send += getTaskDeviceName(event->TaskIndex); + + if (P129_CONFIG_FLAGS_GET_SEPARATE_EVENTS) { + send += '#'; + send += pin; + } + send += '='; + send += state; + send += ','; + send += chip; + send += ','; + send += port; + send += ','; + send += pin; + eventQueue.addMove(std::move(send)); +} + +#endif // ifdef USES_P129 diff --git a/src/src/PluginStructs/P129_data_struct.h b/src/src/PluginStructs/P129_data_struct.h new file mode 100644 index 000000000..fac4753f0 --- /dev/null +++ b/src/src/PluginStructs/P129_data_struct.h @@ -0,0 +1,106 @@ +#ifndef PLUGINSTRUCTS_P129_DATA_STRUCT_H +#define PLUGINSTRUCTS_P129_DATA_STRUCT_H + +#include "../../_Plugin_Helper.h" +#ifdef USES_P129 + +# ifndef LIMIT_BUILD_SIZE +# define P129_DEBUG_LOG // Enable for some (extra) logging +# endif // ifndef LIMIT_BUILD_SIZE + +# define P129_CONFIG_CHIP_COUNT PCONFIG(0) +# define P129_CONFIG_DATA_PIN PIN(0) +# define P129_CONFIG_CLOCK_PIN PIN(1) +# define P129_CONFIG_ENABLE_PIN PIN(2) +# define P129_CONFIG_LOAD_PIN PCONFIG(1) +# define P129_CONFIG_FLAGS PCONFIG(7) // MAX 16 bits!!! + +# define P129_FLAGS_VALUES_DISPLAY 0 // 0/off = HEX, 1/on = BIN +# define P129_FLAGS_READ_FREQUENCY 1 // 0 = 10x/sec, 1 = 50x/sec +# define P129_FLAGS_SEPARATE_EVENTS 2 // Enable/disable separate events per pin [#]=state,chip,port,pin +# define P129_FLAGS_OUTPUT_SELECTION 4 // 0 = decimal & hex/bin, 1 = Decimal, 2= hex/bin + +# define P129_CONFIG_SHOW_OFFSET 0 // Fixed setting + +# define P129_CONFIG_FLAGS_GET_VALUES_DISPLAY (bitRead(P129_CONFIG_FLAGS, P129_FLAGS_VALUES_DISPLAY)) +# define P129_CONFIG_FLAGS_GET_READ_FREQUENCY (bitRead(P129_CONFIG_FLAGS, P129_FLAGS_READ_FREQUENCY)) +# define P129_CONFIG_FLAGS_GET_SEPARATE_EVENTS (bitRead(P129_CONFIG_FLAGS, P129_FLAGS_SEPARATE_EVENTS)) +# define P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION (get4BitFromUL(P129_CONFIG_FLAGS, P129_FLAGS_OUTPUT_SELECTION)) + +# define P129_FREQUENCY_10 0 // 10x per second +# define P129_FREQUENCY_50 1 // 50x per second + +# define P129_OUTPUT_BOTH 0 // Decimal + hex/bin +# define P129_OUTPUT_DEC_ONLY 1 // Decimal +# define P129_OUTPUT_HEXBIN 2 // Hex/bin + +// Number of chips to support undefined = 1, range 1..16 = 8..128 pins +# define P129_MAX_CHIP_COUNT 16 + +# if !defined(P129_MAX_CHIP_COUNT) +# define P129_MAX_CHIP_COUNT 1 // Fallback value +# endif // if !defined(P129_MAX_CHIP_COUNT) +# if P129_MAX_CHIP_COUNT > 16 // Check for max. supported no. of chips +# undef P129_MAX_CHIP_COUNT +# define P129_MAX_CHIP_COUNT 16 +# endif // if P129_MAX_CHIP_COUNT > 16 + +# define P129_SHOW_VALUES // When defined, will show the selected inputs in either Hex or Bin format on the Devices + // page. Disabled when LIMIT_BUILD_SIZE is set +# if defined(LIMIT_BUILD_SIZE) && defined(P129_SHOW_VALUES) +# undef P129_SHOW_VALUES +# endif // if defined(LIMIT_BUILD_SIZE) && defined(P129_SHOW_VALUES) + +struct P129_data_struct : public PluginTaskData_base { +public: + + P129_data_struct(int8_t dataPin, + int8_t clockPin, + int8_t enablePin, + int8_t loadPin, + uint8_t chipCount); + + P129_data_struct() = delete; + ~P129_data_struct(); + + const bool isInitialized() const { // All GPIO's defined + return _dataPin != -1 && + _clockPin != -1 && + _loadPin != -1; + } + + bool plugin_init(struct EventStruct *event); + bool plugin_read(struct EventStruct *event); + bool plugin_write(struct EventStruct *event, + const String & string); + bool plugin_readData(struct EventStruct *event); + +private: + + uint32_t getChannelState(uint8_t offset, + uint8_t size) const; + + bool validChannel(uint channel) const { + return channel > 0 && channel <= (_chipCount * 8); + } + + void checkDiff(struct EventStruct *event); + void sendInputEvent(struct EventStruct *event, + uint8_t group, + uint8_t bit, + uint8_t state); + + + int8_t _dataPin; + int8_t _clockPin; + int8_t _enablePin; + int8_t _loadPin; + uint8_t _chipCount; + + uint8_t readBuffer[P129_MAX_CHIP_COUNT] = { 0 }; + uint8_t prevBuffer[P129_MAX_CHIP_COUNT]; // To compare to +}; + +#endif // ifdef USES_P129 + +#endif // ifndef PLUGINSTRUCTS_P129_DATA_STRUCT_H diff --git a/src/src/PluginStructs/P131_data_struct.cpp b/src/src/PluginStructs/P131_data_struct.cpp new file mode 100644 index 000000000..06823b677 --- /dev/null +++ b/src/src/PluginStructs/P131_data_struct.cpp @@ -0,0 +1,566 @@ +#include "../PluginStructs/P131_data_struct.h" + +#ifdef USES_P131 +# include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object + +/**************************************************************************** + * P131_CommandTrigger_toString: return the command string selected + ***************************************************************************/ +const __FlashStringHelper* P131_CommandTrigger_toString(P131_CommandTrigger cmd) { + switch (cmd) { + case P131_CommandTrigger::neomatrix: return F("neomatrix"); + case P131_CommandTrigger::neo: return F("neo"); + case P131_CommandTrigger::MAX: break; + } + return F("None"); +} + +// **************************************************************************/ +// Constructor +// **************************************************************************/ +P131_data_struct::P131_data_struct(uint8_t matrixWidth, + uint8_t matrixHeight, + uint8_t tileWidth, + uint8_t tileHeight, + int8_t pin, + uint8_t matrixType, + neoPixelType ledType, + uint8_t rotation, + uint8_t fontscaling, + AdaGFXTextPrintMode textmode, + String commandTrigger, + uint8_t brightness, + uint8_t maxbright, + uint16_t fgcolor, + uint16_t bgcolor) + : _matrixWidth(matrixWidth), _matrixHeight(matrixHeight), _tileWidth(tileWidth), _tileHeight(tileHeight), + _pin(pin), _matrixType(matrixType), _ledType(ledType), _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), + _commandTrigger(commandTrigger), _brightness(brightness), _maxbright(maxbright), _fgcolor(fgcolor), _bgcolor(bgcolor) { + updateFontMetrics(); + + _commandTrigger.toLowerCase(); + _commandTriggerCmd = _commandTrigger; + _commandTriggerCmd += F("cmd"); +} + +// **************************************************************************/ +// Destructor +// **************************************************************************/ +P131_data_struct::~P131_data_struct() { + if (isInitialized()) { + delete matrix; // Doesn't have a virtual destructor (yet) + matrix = nullptr; + } +} + +/**************************************************************************** + * plugin_init: Initialize display + ***************************************************************************/ +bool P131_data_struct::plugin_init(struct EventStruct *event) { + bool success = false; + + if (!isInitialized()) { + addLog(LOG_LEVEL_INFO, F("NEOMATRIX: Init start.")); + matrix = new (std::nothrow) Adafruit_NeoMatrix(_matrixWidth, + _matrixHeight, + _tileWidth, + _tileHeight, + _pin, + _matrixType, + _ledType); + + if (isInitialized()) { + _xpix = matrix->width(); + _ypix = matrix->height(); + } + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + String log; + log.reserve(90); + log += F("NEOMATRIX: Init done, address: 0x"); + log += String(reinterpret_cast(matrix), HEX); + log += ' '; + + if (!isInitialized()) { + log += F("in"); + } + log += F("valid, commands: "); + log += _commandTrigger; + log += '/'; + log += _commandTriggerCmd; + log += F(", size: w:"); + log += _xpix; + log += F(", h:"); + log += _ypix; + addLogMove(LOG_LEVEL_INFO, log); + } + # endif // ifndef BUILD_NO_DEBUG + } else { + addLog(LOG_LEVEL_INFO, F("NEOMATRIX: Init failed.")); + } + + if (isInitialized()) { + gfxHelper = new (std::nothrow) AdafruitGFX_helper(matrix, + _commandTrigger, + _xpix, + _ypix, + AdaGFXColorDepth::FullColor, + _textmode, + _fontscaling, + _fgcolor, + _bgcolor, + true, + _textBackFill); + + success = (nullptr != gfxHelper); + + if (success) { + gfxHelper->setRotation(_rotation); + matrix->setBrightness(std::min(_maxbright, _brightness)); // Set brightness, so we don't get blinded by the light + matrix->fillScreen(_bgcolor); // fill screen with black color + matrix->show(); // Update the display + + # ifdef P131_SHOW_SPLASH + + if (P131_CONFIG_FLAG_GET_SHOW_SPLASH) { + uint16_t yPos = 0; + gfxHelper->printText(String(F("ESPEasy")).c_str(), 0, yPos, 1, ADAGFX_WHITE, ADAGFX_BLACK); + matrix->show(); + _splashState = true; // Splash + _splashCounter = P131_SPLASH_DURATION; + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, F("P131 Splash start.")); + # endif // ifndef BUILD_NO_DEBUG + } + # endif // ifdef P131_SHOW_SPLASH + + matrix->setTextColor(_fgcolor, _bgcolor); // set text color to white and black background + gfxHelper->setColumnRowMode(false); // Pixel-mode + matrix->setTextSize(_fontscaling); // Handles 0 properly, text size, default 1 + matrix->setCursor(0, 0); // move cursor to position (0, 0) pixel + updateFontMetrics(); + + // Load + loadContent(event); + + // Setup initial scroll position + for (uint8_t x = 0; x < P131_CONFIG_TILE_HEIGHT; x++) { + content[x].pixelPos = 0; + + if (content[x].active) { + String tmpString = parseStringKeepCase(strings[x], 1); + String newString = AdaGFXparseTemplate(tmpString, _textcols, gfxHelper); + uint16_t h; + content[x].length = gfxHelper->getTextSize(newString, h); + + if (content[x].startBlank && (content[x].length > _xpix)) { + if (content[x].rightScroll) { + content[x].pixelPos = -1 * content[x].length; + } else { + content[x].pixelPos = _xpix; + } + } + } + } + } + } + return success; +} + +/**************************************************************************** + * plugin_exit: De-initialize before destruction + ***************************************************************************/ +bool P131_data_struct::plugin_exit(struct EventStruct *event) { + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, F("NEOMATRIX: Exit.")); + # endif // ifndef BUILD_NO_DEBUG + + if ((nullptr != matrix) && bitRead(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_CLEAR_ON_EXIT)) { + matrix->fillScreen(ADAGFX_BLACK); // fill screen with black color + matrix->show(); + } + cleanup(); + return true; +} + +/**************************************************************************** + * cleanup: De-initialize pointers + ***************************************************************************/ +void P131_data_struct::cleanup() { + delete gfxHelper; + gfxHelper = nullptr; + + delete matrix; // Doesn't have a virtual destructor (yet) + matrix = nullptr; +} + +/**************************************************************************** + * loadContent: load the default content if not yet loaded + ***************************************************************************/ +void P131_data_struct::loadContent(struct EventStruct *event) { + if (!stringsInitialized) { + LoadCustomTaskSettings(event->TaskIndex, strings, P131_Nlines, 0); + stringsInitialized = true; + } + + if (!contentInitialized && stringsInitialized) { + content.clear(); + content.resize(P131_CONFIG_TILE_HEIGHT); + + for (uint8_t x = 0; x < P131_CONFIG_TILE_HEIGHT; x++) { + content[x] = P131_content_struct(); + initialize_content(event, x); + } + contentInitialized = true; + } +} + +/**************************************************************************** + * initialize_content: set the content[x] flags from arguments provided + ***************************************************************************/ +void P131_data_struct::initialize_content(struct EventStruct *event, + uint8_t x) { + String opts = parseString(strings[x], 2); + uint32_t optBits = 0; + + validUIntFromString(opts, optBits); + content[x].active = bitRead(optBits, P131_OPTBITS_SCROLL); + content[x].rightScroll = bitRead(optBits, P131_OPTBITS_RIGHTSCROLL); + content[x].startBlank = bitRead(optBits, P131_OPTBITS_STARTBLANK) == 0; // Inverted + content[x].stepWidth = get4BitFromUL(optBits, P131_OPTBITS_SCROLLSTEP) + 1; // Add offset once + opts = parseString(strings[x], 3); + int speed = 0; + + validIntFromString(opts, speed); + content[x].speed = speed; +} + +/**************************************************************************** + * plugin_read: Re-draw the default content + ***************************************************************************/ +bool P131_data_struct::plugin_read(struct EventStruct *event) { + if (isInitialized() && !_splashState) { + loadContent(event); + + bool hasContent = false; + + for (uint8_t x = 0; x < P131_CONFIG_TILE_HEIGHT && !hasContent; x++) { + hasContent = !parseStringKeepCase(strings[x], 1).isEmpty(); + } + + if (hasContent) { + display_content(event); + } + } + return false; // Always return false, so no attempt to send to + // Controllers or generate events is started +} + +/**************************************************************************** + * display_content: Re-display the text, and apply any scrolling offset + ***************************************************************************/ +void P131_data_struct::display_content(struct EventStruct *event, + bool scrollOnly, + uint8_t line) { + if (isInitialized() && (nullptr != gfxHelper)) { + int16_t yPos = 0; + bool useVal = gfxHelper->getValidation(); + gfxHelper->setValidation(false); // Ignore validation to enable scrolling + + uint8_t x = 0; + uint8_t x_end = P131_CONFIG_TILE_HEIGHT; + + if (line != 255) { + x = line; + x_end = line + 1; + } + + for (; x < x_end; x++) { + if (!scrollOnly || + (scrollOnly && content[x].active)) { + String tmpString = parseStringKeepCase(strings[x], 1); + String newString = AdaGFXparseTemplate(tmpString, _textcols, gfxHelper); + uint16_t h; + content[x].length = gfxHelper->getTextSize(newString, h); + + # if ADAGFX_PARSE_SUBCOMMAND + updateFontMetrics(); + # endif // if ADAGFX_PARSE_SUBCOMMAND + + if (yPos < _ypix) { + gfxHelper->printText(newString.c_str(), + content[x].pixelPos, + yPos, + _fontscaling, + _fgcolor, + _bgcolor); + + if (scrollOnly && content[x].active && (content[x].length > _xpix)) { + if (content[x].rightScroll && (content[x].pixelPos > 0)) { + // Clear left from text + matrix->fillRect(content[x].pixelPos - content[x].stepWidth, + yPos, + content[x].stepWidth, + h, + _bgcolor); + } + + if (!content[x].rightScroll && (content[x].pixelPos + content[x].length < _xpix) && (content[x].stepWidth > 1)) { + // Clear right from text + matrix->fillRect(content[x].pixelPos + content[x].length + 1, + yPos, + content[x].stepWidth - 1, + h, + _bgcolor); + } + } + } + + if (scrollOnly && content[x].active) { + if (content[x].rightScroll) { + // Fully scrolled? then reset, starting left of the screen or with right side aligned right if not startBlank + if (content[x].pixelPos > (content[x].startBlank ? _xpix : 0 - content[x].stepWidth)) { + if (content[x].startBlank) { + content[x].pixelPos = -1 * content[x].length; + } else { + content[x].pixelPos = (-1 * content[x].length) + _xpix - content[x].stepWidth; + } + } + } else { + // Fully scrolled? then reset, starting at right of the screen or left if not startBlank + if (content[x].pixelPos + content[x].length < (content[x].startBlank ? 0 : _xpix + content[x].stepWidth)) { + if (content[x].startBlank) { + content[x].pixelPos = _xpix; + } else { + content[x].pixelPos = content[x].stepWidth; + } + } + } + + // Logging used only during development + // String log = F("display_content: x="); + // log += x; + // log += F(", pxPos="); + // log += content[x].pixelPos; + // log += F(", len="); + // log += content[x].length; + // log += F(", stp="); + // log += content[x].stepWidth; + // log += F(", xpix="); + // log += _xpix; + // addLogMove(LOG_LEVEL_INFO, log); + } + } + delay(0); + yPos += (_fontheight * _fontscaling); + } + gfxHelper->setValidation(useVal); + matrix->show(); + } +} + +/**************************************************************************** + * plugin_write: Handle commands + ***************************************************************************/ +bool P131_data_struct::plugin_write(struct EventStruct *event, const String& string) { + bool success = false; + + if (isInitialized() && !_splashState) { + const String cmd = parseString(string, 1); + + if ((nullptr != matrix) && cmd.equals(_commandTriggerCmd)) { + String sub = parseString(string, 2); + int16_t x = event->Par2 - 1; + success = true; + + if (sub.equals(F("clear"))) { + matrix->fillScreen(_bgcolor); + } else if (sub.startsWith(F("bright")) && (event->Par2 >= 0) && (event->Par2 <= 255)) { + if (parseString(string, 3).isEmpty()) { // No argument, then + matrix->setBrightness(std::min(_maxbright, _brightness)); // use initial brightness + } else { + matrix->setBrightness(std::min(_maxbright, static_cast(event->Par2))); + } + } else if (sub.equals(F("settext")) + && ((event->Par2 > 0) && (event->Par2 <= P131_CONFIG_TILE_HEIGHT))) { // line + String tmpString = parseStringToEnd(strings[x], 2); // settings to be transferred + strings[x] = wrapWithQuotesIfContainsParameterSeparatorChar(parseStringToEndKeepCase(string, 4)); + strings[x] += ','; + strings[x] += tmpString; + } else if ((sub.equals(F("setscroll")) || // neomatrixcmd,setscroll,,0|1 + sub.equals(F("setempty")) || // neomatrixcmd,setempty,,0|1 + sub.equals(F("setright")) // neomatrixcmd,setright,,0|1 + ) + && ((event->Par2 > 0) && (event->Par2 <= P131_CONFIG_TILE_HEIGHT)) // line + && ((event->Par3 >= 0) && (event->Par3 <= 1))) { // on/off + String tmpString1 = parseStringKeepCase(strings[x], 1); // settings to be transferred + String tmpString3 = parseString(strings[x], 3); // settings to be transferred + String opts = parseString(strings[x], 2); + uint32_t optBits = 0; + + validUIntFromString(opts, optBits); + + if (sub[3] == 's') { // setscroll + bitWrite(optBits, P131_OPTBITS_SCROLL, event->Par3 == 1); + } else if (sub[3] == 'e') { // setempty + bitWrite(optBits, P131_OPTBITS_STARTBLANK, event->Par3 == 0); // Inverted value + } else if (sub[3] == 'r') { // setright + bitWrite(optBits, P131_OPTBITS_RIGHTSCROLL, event->Par3 == 1); + } + strings[x] = wrapWithQuotesIfContainsParameterSeparatorChar(tmpString1); + strings[x] += ','; + strings[x] += optBits; + strings[x] += ','; + strings[x] += tmpString3; + } else if (sub.equals(F("setstep")) // neomatrixcmd,setstep,,1..16 + && ((event->Par2 > 0) && (event->Par2 <= P131_CONFIG_TILE_HEIGHT)) // line + && ((event->Par3 > 0) && (event->Par3 <= P131_MAX_SCROLL_STEPS))) { // 1..16 + String tmpString1 = parseStringKeepCase(strings[x], 1); // settings to be transferred + String tmpString3 = parseString(strings[x], 3); // settings to be transferred + String opts = parseString(strings[x], 2); + uint32_t optBits = 0; + + validUIntFromString(opts, optBits); + set4BitToUL(optBits, P131_OPTBITS_SCROLLSTEP, event->Par3 - 1); // Use offset of -1 + strings[x] = wrapWithQuotesIfContainsParameterSeparatorChar(tmpString1); + strings[x] += ','; + strings[x] += optBits; + strings[x] += ','; + strings[x] += tmpString3; + } else if (sub.equals(F("setspeed")) // neomatrixcmd,setspeed,,1..600 + && ((event->Par2 > 0) && (event->Par2 <= P131_CONFIG_TILE_HEIGHT)) // line + && ((event->Par3 > 0) && (event->Par3 <= P131_MAX_SCROLL_SPEED))) { // 1..600 + String tmpString1 = parseStringKeepCase(strings[x], 1); // settings to be transferred + String tmpString2 = parseString(strings[x], 2); // settings to be transferred + strings[x] = wrapWithQuotesIfContainsParameterSeparatorChar(tmpString1); + strings[x] += ','; + strings[x] += tmpString2; + strings[x] += ','; + strings[x] += event->Par3; + } else { + success = false; + } + + if (success && sub.startsWith(F("set"))) { + initialize_content(event, x); // Set up line parameters + content[x].loop = -1; // Restart loop + display_content(event, false, x); // (re-)display line + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + String log = F("NEOMATRIX: set line "); + log += event->Par2; + log += F(": "); + log += strings[x]; + addLogMove(LOG_LEVEL_INFO, log); + } + # endif // ifndef BUILD_NO_DEBUG + } + } + else if (matrix && (cmd.equals(_commandTrigger) || + (gfxHelper && gfxHelper->isAdaGFXTrigger(cmd)))) { + success = true; + + if (nullptr != gfxHelper) { + String tmp(string); + + // Hand it over after replacing variables + success = gfxHelper->processCommand(AdaGFXparseTemplate(tmp, _textcols, gfxHelper)); + + updateFontMetrics(); // Font or color may have changed + } + } + + if (success) { + matrix->show(); + } + } + return success; +} + +/**************************************************************************** + * plugin_ten_per_second: Re-draw the default content that should be scrolled + ***************************************************************************/ +bool P131_data_struct::plugin_ten_per_second(struct EventStruct *event) { + bool success = false; + + # ifdef P131_SHOW_SPLASH + + if (_splashState) { // Decrement splash counter + _splashCounter--; + _splashState = _splashCounter != 0; + + if (!_splashState) { + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, F("P131 Splash finished.")); + # endif // ifndef BUILD_NO_DEBUG + + if (nullptr != matrix) { + matrix->fillScreen(_bgcolor); // fill screen with black color + } + + // Schedule the surrogate initial PLUGIN_READ that has been suppressed by the splash + Scheduler.schedule_task_device_timer(event->TaskIndex, millis() + 10); + } + } + # endif // ifdef P131_SHOW_SPLASH + + if (isInitialized() && !_splashState) { + loadContent(event); + success = true; + + for (uint8_t x = 0; x < P131_CONFIG_TILE_HEIGHT; x++) { + if (content[x].active && (content[x].length > _xpix)) { + if (content[x].loop == -1) { content[x].loop = content[x].speed; } // Initialize + + if (!content[x].loop--) { + content[x].pixelPos += (content[x].rightScroll ? 1 : -1) * content[x].stepWidth; + + display_content(event, true); + } + } + } + } + return success; +} + +/**************************************************************************** + * updateFontMetrics: recalculate x and y columns, based on font size and font scale + ***************************************************************************/ +void P131_data_struct::updateFontMetrics() { + if (_fontscaling == 0) { _fontscaling = 1; } // Sanity checks + + if (_fontwidth == 0) { _fontwidth = 6; } + + if (_fontheight == 0) { _fontheight = 10; } + + if (nullptr != gfxHelper) { + gfxHelper->getTextMetrics(_textcols, _textrows, _fontwidth, _fontheight, _fontscaling, _heightOffset, _xpix, _ypix); + gfxHelper->getColors(_fgcolor, _bgcolor); + } else { + _textcols = _xpix / (_fontwidth * _fontscaling); + _textrows = _ypix / (_fontheight * _fontscaling); + } + # ifdef P131_DEBUG_LOG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + String log = F("updateFontMetrics: size: "); + log += _fontscaling; + log += F(", fg: "); + log += String(_fgcolor, HEX); // AdaGFXcolorToString(_fgcolor); + log += F(", bg: "); + log += String(_bgcolor, HEX); // AdaGFXcolorToString(_bgcolor); + + if (nullptr != matrix) { + log += F(", xp: "); + log += matrix->getCursorX(); + log += F(", yp: "); + log += matrix->getCursorY(); + } + addLogMove(LOG_LEVEL_DEBUG, log); + } + # endif // ifdef P131_DEBUG_LOG +} + +#endif // ifdef USES_P131 diff --git a/src/src/PluginStructs/P131_data_struct.h b/src/src/PluginStructs/P131_data_struct.h new file mode 100644 index 000000000..e2a117207 --- /dev/null +++ b/src/src/PluginStructs/P131_data_struct.h @@ -0,0 +1,177 @@ +#ifndef PLUGINSTRUCTS_P131_DATA_STRUCT_H +#define PLUGINSTRUCTS_P131_DATA_STRUCT_H + +#include "../../_Plugin_Helper.h" +#ifdef USES_P131 + +# include +# include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object +# include "../CustomBuild/StorageLayout.h" // Available custom storage + +# include + +# define P131_DEBUG_LOG // Enable for some (extra) logging + +# define P131_Nlines 16 // The number of different lines which can be displayed +# define P131_Nchars 50 + +// # define P131_SHOW_SPLASH // Enable to show splash (text) +# define P131_SPLASH_DURATION (3000 / 100) // 3 seconds in 100 millisecond chunks + +# define P131_CONFIG_MATRIX_WIDTH PCONFIG(0) +# define P131_CONFIG_MATRIX_HEIGHT PCONFIG(1) +# define P131_CONFIG_TILE_WIDTH PCONFIG(2) +# define P131_CONFIG_TILE_HEIGHT PCONFIG(3) + +# define P131_CONFIG_FLAGS PCONFIG_ULONG(0) +# define P131_CONFIG_FLAGS_B PCONFIG_ULONG(1) +# define P131_CONFIG_COLORS PCONFIG_ULONG(3) // 2 Colors fit in 1 long + +# define P131_MAX_SCROLL_STEPS 16 // Max. stepsize +# define P131_MAX_SCROLL_SPEED 600 // Max. speed + +# define P131_FLAGS_MATRIX_TYPE 0 // MatrixType flags +# define P131_FLAGS_MATRIX_TYPE_TOP 0 // MatrixType flags Top/Bottom/Left/Right +# define P131_FLAGS_MATRIX_TYPE_RC 2 // MatrixType Row/Col +# define P131_FLAGS_MATRIX_TYPE_PZ 3 // MatrixType Progressive/Zigzag +# define P131_FLAGS_TILE_TYPE 4 // TileType flags +# define P131_FLAGS_TILE_TYPE_TOP 4 // TileType Top/Bottom/Left/Right +# define P131_FLAGS_TILE_TYPE_RC 6 // TileType RowCol +# define P131_FLAGS_TILE_TYPE_PZ 7 // TileType Progressive/Zigzag +# define P131_CONFIG_FLAG_ROTATION 8 // Flag-offset to store 4 bits for Rotation, uses bits 8, 9, 10, 11 +# define P131_CONFIG_FLAG_MODE 12 // Flag-offset to store 4 bits for Mode, uses bits 12, 13, 14 and 15 +# define P131_CONFIG_FLAG_FONTSCALE 20 // Flag-offset to store 4 bits for Font scaling, uses bits 20, 21, 22, 23 +# define P131_CONFIG_FLAG_CMD_TRIGGER 24 // Flag-offset to store 4 bits for Command trigger, uses bits 24, 25, 26, 27 +# define P131_CONFIG_FLAG_CLEAR_ON_EXIT 28 // Flag: Clear display on exit +# define P131_CONFIG_FLAG_STRIP_TYPE 29 // Flag: Strip type +# define P131_CONFIG_FLAG_SHOW_SPLASH 30 // Flag: Show splash + +# define P131_CONFIG_FLAG_B_BRIGHTNESS 0 // Brightness, 8 bits +# define P131_CONFIG_FLAG_B_MAXBRIGHT 8 // Maximum Brightness, 8 bits + +# define P131_CONFIG_FLAGS_GET_MATRIX_TYPE (get8BitFromUL(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE)) +# define P131_CONFIG_FLAG_GET_ROTATION (get4BitFromUL(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_ROTATION)) +# define P131_CONFIG_FLAG_GET_FONTSCALE (get4BitFromUL(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_FONTSCALE)) +# define P131_CONFIG_FLAG_GET_CMD_TRIGGER (get4BitFromUL(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_CMD_TRIGGER)) +# define P131_CONFIG_FLAGS_GET_STRIP_TYPE (bitRead(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_STRIP_TYPE)) + +# define P131_CONFIG_FLAG_GET_MODE (get4BitFromUL(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_MODE)) +# define P131_CONFIG_FLAG_GET_BRIGHTNESS (get8BitFromUL(P131_CONFIG_FLAGS_B, P131_CONFIG_FLAG_B_BRIGHTNESS)) +# define P131_CONFIG_FLAG_GET_MAXBRIGHT (get8BitFromUL(P131_CONFIG_FLAGS_B, P131_CONFIG_FLAG_B_MAXBRIGHT)) +# define P131_CONFIG_FLAG_GET_SHOW_SPLASH (!bitRead(P131_CONFIG_FLAGS, P131_CONFIG_FLAG_SHOW_SPLASH)) // Inverted setting, default on + +# define P131_CONFIG_GET_COLOR_FOREGROUND (P131_CONFIG_COLORS & 0xFFFF) +# define P131_CONFIG_GET_COLOR_BACKGROUND ((P131_CONFIG_COLORS >> 16) & 0xFFFF) + +// Line options +# define P131_OPTBITS_SCROLL 0 // Scrolling enabled +# define P131_OPTBITS_RIGHTSCROLL 1 // Scroll in from right +# define P131_OPTBITS_STARTBLANK 3 // Start with blank display (1) or not (0) +# define P131_OPTBITS_SCROLLSTEP 4 // Number of pixels to scroll + +enum class P131_CommandTrigger : uint8_t { + neomatrix = 0u, + neo = 1u, + MAX = 2u // Keep as last item! +}; + +const __FlashStringHelper* P131_CommandTrigger_toString(P131_CommandTrigger cmd); + +struct P131_content_struct { + int16_t length = 0; // Intermediate storage for current length + int16_t position = 0; // position within the string to display (characters) + int16_t pixelPos = 0; // current left-offset on display + int16_t speed = 0; // 0.1 sec. steps + int16_t loop = -1; // steps before we go, -1 = restart from speed + int8_t stepWidth = 0; // Nr. of pixels to scroll - 1 + bool active = false; + bool rightScroll = false; + bool startBlank = false; +}; + +struct P131_data_struct : public PluginTaskData_base { +public: + + P131_data_struct(uint8_t matrixWidth, + uint8_t matrixHeight, + uint8_t tileWidth, + uint8_t tileHeight, + int8_t pin, + uint8_t matrixType, + neoPixelType ledType, + uint8_t rotation, + uint8_t fontscaling, + AdaGFXTextPrintMode textmode, + String commandTrigger, + uint8_t brightness, + uint8_t maxbright, + uint16_t fgcolor = ADAGFX_WHITE, + uint16_t bgcolor = ADAGFX_BLACK); + + P131_data_struct() = delete; + ~P131_data_struct(); + + bool plugin_init(struct EventStruct *event); + bool plugin_exit(struct EventStruct *event); + bool plugin_read(struct EventStruct *event); + bool plugin_write(struct EventStruct *event, + const String & string); + bool plugin_ten_per_second(struct EventStruct *event); + + bool isInitialized() { + return matrix != nullptr; + } + +private: + + void updateFontMetrics(); + void cleanup(); + void display_content(struct EventStruct *event, + bool scrollOnly = false, + uint8_t line = 255); + void loadContent(struct EventStruct *event); + void initialize_content(struct EventStruct *event, + uint8_t x); + + Adafruit_NeoMatrix *matrix = nullptr; + AdafruitGFX_helper *gfxHelper = nullptr; + + uint8_t _matrixWidth = 8; + uint8_t _matrixHeight = 8; + uint8_t _tileWidth = 1; + uint8_t _tileHeight = 1; + int8_t _pin = -1; + uint8_t _matrixType = NEO_MATRIX_TOP | NEO_MATRIX_LEFT | NEO_MATRIX_ROWS | NEO_MATRIX_PROGRESSIVE; + uint8_t _ledType = NEO_TILE_TOP | NEO_TILE_LEFT | NEO_TILE_ROWS | NEO_TILE_PROGRESSIVE; + uint8_t _rotation = 0; + uint8_t _fontscaling = 1; + AdaGFXTextPrintMode _textmode = AdaGFXTextPrintMode::ContinueToNextLine; + String _commandTrigger; + uint8_t _brightness = 40; + uint8_t _maxbright = 255; + uint16_t _fgcolor = ADAGFX_WHITE; + uint16_t _bgcolor = ADAGFX_BLACK; + + uint16_t _textcols = 0; + uint16_t _textrows = 0; + uint16_t _xpix = 0u; + uint16_t _ypix = 0u; + uint8_t _fontwidth = 6; // Default font characteristics + uint8_t _fontheight = 10; + uint8_t _heightOffset = 0; + bool _textBackFill = false; + String _commandTriggerCmd; + + String strings[P131_Nlines]; // Read once + bool stringsInitialized = false; + + std::vectorcontent; + bool contentInitialized = false; + + bool _splashState = false; // Have this always available to avoid 'many' #ifdefs in the code + # ifdef P131_SHOW_SPLASH + uint8_t _splashCounter = P131_SPLASH_DURATION; + # endif // ifdef P131_SHOW_SPLASH +}; +#endif // ifdef USES_P131 +#endif // ifndef PLUGINSTRUCTS_P131_DATA_STRUCT_H diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index dc8a410f9..a2900f83a 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -85,7 +85,7 @@ static const char DATA_GITHUB_CLIPBOARD_JS[] PROGMEM = {0x66,0x75,0x6e,0x63,0x74 #endif #if defined(WEBSERVER_CSS) && !defined(WEBSERVER_EMBED_CUSTOM_CSS) -static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = {0x2e,0x63,0x6c,0x6f,0x73,0x65,0x62,0x74,0x6e,0x2c,0x68,0x31,0x2c,0x68,0x32,0x2c,0x68,0x33,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x77,0x65,0x69,0x67,0x68,0x74,0x3a,0x37,0x30,0x30,0x7d,0x68,0x31,0x2c,0x68,0x36,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x37,0x44,0x7d,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x2c,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x7b,0x63,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x3a,0x27,0x27,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x2c,0x2e,0x6d,0x65,0x6e,0x75,0x7b,0x74,0x65,0x78,0x74,0x2d,0x64,0x65,0x63,0x6f,0x72,0x61,0x74,0x69,0x6f,0x6e,0x3a,0x6e,0x6f,0x6e,0x65,0x7d,0x2e,0x64,0x69,0x76,0x5f,0x6c,0x2c,0x2e,0x6d,0x65,0x6e,0x75,0x7b,0x66,0x6c,0x6f,0x61,0x74,0x3a,0x6c,0x65,0x66,0x74,0x7d,0x2e,0x63,0x6c,0x6f,0x73,0x65,0x62,0x74,0x6e,0x2c,0x2e,0x64,0x69,0x76,0x5f,0x72,0x7b,0x66,0x6c,0x6f,0x61,0x74,0x3a,0x72,0x69,0x67,0x68,0x74,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x66,0x66,0x66,0x7d,0x2a,0x7b,0x62,0x6f,0x78,0x2d,0x73,0x69,0x7a,0x69,0x6e,0x67,0x3a,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x62,0x6f,0x78,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x66,0x61,0x6d,0x69,0x6c,0x79,0x3a,0x73,0x61,0x6e,0x73,0x2d,0x73,0x65,0x72,0x69,0x66,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x32,0x70,0x74,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x30,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x30,0x7d,0x68,0x31,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x36,0x70,0x74,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x38,0x70,0x78,0x20,0x30,0x7d,0x68,0x32,0x2c,0x68,0x33,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x32,0x70,0x74,0x7d,0x68,0x32,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x34,0x34,0x34,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x46,0x46,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x30,0x20,0x2d,0x34,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x36,0x70,0x78,0x7d,0x68,0x33,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x45,0x45,0x45,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x34,0x34,0x34,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x31,0x36,0x70,0x78,0x20,0x2d,0x34,0x70,0x78,0x20,0x30,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x7d,0x68,0x36,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x30,0x70,0x74,0x7d,0x63,0x6f,0x64,0x65,0x2c,0x6b,0x62,0x64,0x2c,0x70,0x72,0x65,0x2c,0x73,0x61,0x6d,0x70,0x2c,0x74,0x74,0x2c,0x78,0x6d,0x70,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x66,0x61,0x6d,0x69,0x6c,0x79,0x3a,0x6d,0x6f,0x6e,0x6f,0x73,0x70,0x61,0x63,0x65,0x2c,0x6d,0x6f,0x6e,0x6f,0x73,0x70,0x61,0x63,0x65,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x65,0x6d,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x37,0x44,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x34,0x70,0x78,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x46,0x46,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x34,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x20,0x31,0x36,0x70,0x78,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x2e,0x68,0x65,0x6c,0x70,0x2c,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x2c,0x69,0x6e,0x70,0x75,0x74,0x2c,0x73,0x65,0x6c,0x65,0x63,0x74,0x2c,0x74,0x65,0x78,0x74,0x61,0x72,0x65,0x61,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x67,0x72,0x61,0x79,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x73,0x74,0x79,0x6c,0x65,0x3a,0x73,0x6f,0x6c,0x69,0x64,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x70,0x78,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x2e,0x6c,0x69,0x6e,0x6b,0x2e,0x77,0x69,0x64,0x65,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x69,0x6e,0x6c,0x69,0x6e,0x65,0x2d,0x62,0x6c,0x6f,0x63,0x6b,0x3b,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x30,0x30,0x25,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x2e,0x6c,0x69,0x6e,0x6b,0x2e,0x72,0x65,0x64,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x72,0x65,0x64,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x2e,0x68,0x65,0x6c,0x70,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x35,0x30,0x25,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x32,0x70,0x78,0x20,0x34,0x70,0x78,0x7d,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x2c,0x69,0x6e,0x70,0x75,0x74,0x2c,0x73,0x65,0x6c,0x65,0x63,0x74,0x2c,0x74,0x65,0x78,0x74,0x61,0x72,0x65,0x61,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x34,0x70,0x78,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x3a,0x68,0x6f,0x76,0x65,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x3a,0x23,0x33,0x36,0x39,0x7d,0x69,0x6e,0x70,0x75,0x74,0x3a,0x68,0x6f,0x76,0x65,0x72,0x2c,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x68,0x6f,0x76,0x65,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x63,0x63,0x63,0x7d,0x69,0x6e,0x70,0x75,0x74,0x2c,0x73,0x65,0x6c,0x65,0x63,0x74,0x2c,0x74,0x65,0x78,0x74,0x61,0x72,0x65,0x61,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x65,0x65,0x65,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x34,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x20,0x38,0x70,0x78,0x7d,0x69,0x6e,0x70,0x75,0x74,0x2e,0x77,0x69,0x64,0x65,0x2c,0x73,0x65,0x6c,0x65,0x63,0x74,0x2e,0x77,0x69,0x64,0x65,0x7b,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x35,0x30,0x30,0x70,0x78,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x38,0x30,0x25,0x7d,0x2e,0x77,0x69,0x64,0x65,0x6e,0x75,0x6d,0x62,0x65,0x72,0x7b,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x35,0x30,0x30,0x70,0x78,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x37,0x65,0x6d,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x2c,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x32,0x70,0x74,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x2d,0x6c,0x65,0x66,0x74,0x3a,0x33,0x35,0x70,0x78,0x3b,0x63,0x75,0x72,0x73,0x6f,0x72,0x3a,0x70,0x6f,0x69,0x6e,0x74,0x65,0x72,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x7b,0x2d,0x6d,0x6f,0x7a,0x2d,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x2d,0x6d,0x73,0x2d,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x2d,0x77,0x65,0x62,0x6b,0x69,0x74,0x2d,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x62,0x6c,0x6f,0x63,0x6b,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x6c,0x65,0x66,0x74,0x3a,0x34,0x70,0x78,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x74,0x6f,0x70,0x3a,0x30,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x72,0x65,0x6c,0x61,0x74,0x69,0x76,0x65,0x3b,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x20,0x69,0x6e,0x70,0x75,0x74,0x7b,0x63,0x75,0x72,0x73,0x6f,0x72,0x3a,0x70,0x6f,0x69,0x6e,0x74,0x65,0x72,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x30,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x7d,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x2e,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x64,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x67,0x72,0x65,0x79,0x7d,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x65,0x65,0x65,0x3b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x32,0x35,0x70,0x78,0x3b,0x6c,0x65,0x66,0x74,0x3a,0x30,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x3b,0x74,0x6f,0x70,0x3a,0x30,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x32,0x35,0x70,0x78,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x3a,0x68,0x6f,0x76,0x65,0x72,0x20,0x69,0x6e,0x70,0x75,0x74,0x7e,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x63,0x63,0x63,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x20,0x69,0x6e,0x70,0x75,0x74,0x3a,0x63,0x68,0x65,0x63,0x6b,0x65,0x64,0x7e,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x37,0x44,0x7d,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x20,0x69,0x6e,0x70,0x75,0x74,0x3a,0x63,0x68,0x65,0x63,0x6b,0x65,0x64,0x7e,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x2c,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x62,0x6c,0x6f,0x63,0x6b,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x20,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x7b,0x2d,0x6d,0x73,0x2d,0x74,0x72,0x61,0x6e,0x73,0x66,0x6f,0x72,0x6d,0x3a,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x34,0x35,0x64,0x65,0x67,0x29,0x3b,0x2d,0x77,0x65,0x62,0x6b,0x69,0x74,0x2d,0x74,0x72,0x61,0x6e,0x73,0x66,0x6f,0x72,0x6d,0x3a,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x34,0x35,0x64,0x65,0x67,0x29,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x3a,0x73,0x6f,0x6c,0x69,0x64,0x20,0x23,0x66,0x66,0x66,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x30,0x20,0x33,0x70,0x78,0x20,0x33,0x70,0x78,0x20,0x30,0x3b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x31,0x30,0x70,0x78,0x3b,0x6c,0x65,0x66,0x74,0x3a,0x37,0x70,0x78,0x3b,0x74,0x6f,0x70,0x3a,0x33,0x70,0x78,0x3b,0x74,0x72,0x61,0x6e,0x73,0x66,0x6f,0x72,0x6d,0x3a,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x34,0x35,0x64,0x65,0x67,0x29,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x35,0x70,0x78,0x7d,0x23,0x74,0x6f,0x61,0x73,0x74,0x6d,0x65,0x73,0x73,0x61,0x67,0x65,0x2c,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x2c,0x2e,0x6c,0x6f,0x67,0x76,0x69,0x65,0x77,0x65,0x72,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x67,0x72,0x61,0x79,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x73,0x74,0x79,0x6c,0x65,0x3a,0x73,0x6f,0x6c,0x69,0x64,0x7d,0x23,0x74,0x6f,0x61,0x73,0x74,0x6d,0x65,0x73,0x73,0x61,0x67,0x65,0x2c,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x70,0x78,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x7b,0x2d,0x6d,0x6f,0x7a,0x2d,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x2d,0x6d,0x73,0x2d,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x2d,0x77,0x65,0x62,0x6b,0x69,0x74,0x2d,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x32,0x30,0x70,0x78,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x6c,0x65,0x66,0x74,0x3a,0x39,0x70,0x78,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x72,0x65,0x6c,0x61,0x74,0x69,0x76,0x65,0x3b,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x7b,0x63,0x75,0x72,0x73,0x6f,0x72,0x3a,0x70,0x6f,0x69,0x6e,0x74,0x65,0x72,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x30,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x7d,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x65,0x65,0x65,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x35,0x30,0x25,0x3b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x32,0x36,0x70,0x78,0x3b,0x6c,0x65,0x66,0x74,0x3a,0x30,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x3b,0x74,0x6f,0x70,0x3a,0x30,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x32,0x36,0x70,0x78,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x3a,0x68,0x6f,0x76,0x65,0x72,0x20,0x69,0x6e,0x70,0x75,0x74,0x7e,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x63,0x63,0x63,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x3a,0x63,0x68,0x65,0x63,0x6b,0x65,0x64,0x7e,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x37,0x44,0x7d,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x3a,0x63,0x68,0x65,0x63,0x6b,0x65,0x64,0x7e,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x62,0x6c,0x6f,0x63,0x6b,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x20,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x3a,0x23,0x66,0x66,0x66,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x35,0x30,0x25,0x3b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x38,0x70,0x78,0x3b,0x6c,0x65,0x66,0x74,0x3a,0x38,0x70,0x78,0x3b,0x74,0x6f,0x70,0x3a,0x38,0x70,0x78,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x38,0x70,0x78,0x7d,0x2e,0x6c,0x6f,0x67,0x76,0x69,0x65,0x77,0x65,0x72,0x2c,0x74,0x65,0x78,0x74,0x61,0x72,0x65,0x61,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x66,0x61,0x6d,0x69,0x6c,0x79,0x3a,0x27,0x4c,0x75,0x63,0x69,0x64,0x61,0x20,0x43,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x27,0x2c,0x4d,0x6f,0x6e,0x61,0x63,0x6f,0x2c,0x6d,0x6f,0x6e,0x6f,0x73,0x70,0x61,0x63,0x65,0x3b,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x30,0x30,0x30,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x20,0x38,0x70,0x78,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x38,0x30,0x25,0x7d,0x23,0x74,0x6f,0x61,0x73,0x74,0x6d,0x65,0x73,0x73,0x61,0x67,0x65,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x37,0x44,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x34,0x70,0x78,0x3b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x33,0x30,0x25,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x66,0x66,0x66,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x37,0x70,0x78,0x3b,0x6c,0x65,0x66,0x74,0x3a,0x32,0x38,0x32,0x70,0x78,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x6c,0x65,0x66,0x74,0x3a,0x2d,0x31,0x32,0x35,0x70,0x78,0x3b,0x6d,0x69,0x6e,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x32,0x35,0x30,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x31,0x36,0x70,0x78,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x66,0x69,0x78,0x65,0x64,0x3b,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x76,0x69,0x73,0x69,0x62,0x69,0x6c,0x69,0x74,0x79,0x3a,0x68,0x69,0x64,0x64,0x65,0x6e,0x3b,0x7a,0x2d,0x69,0x6e,0x64,0x65,0x78,0x3a,0x31,0x7d,0x23,0x74,0x6f,0x61,0x73,0x74,0x6d,0x65,0x73,0x73,0x61,0x67,0x65,0x2e,0x73,0x68,0x6f,0x77,0x7b,0x2d,0x77,0x65,0x62,0x6b,0x69,0x74,0x2d,0x61,0x6e,0x69,0x6d,0x61,0x74,0x69,0x6f,0x6e,0x3a,0x66,0x61,0x64,0x65,0x69,0x6e,0x20,0x2e,0x35,0x73,0x2c,0x66,0x61,0x64,0x65,0x6f,0x75,0x74,0x20,0x2e,0x35,0x73,0x20,0x32,0x2e,0x35,0x73,0x3b,0x61,0x6e,0x69,0x6d,0x61,0x74,0x69,0x6f,0x6e,0x3a,0x66,0x61,0x64,0x65,0x69,0x6e,0x20,0x2e,0x35,0x73,0x2c,0x66,0x61,0x64,0x65,0x6f,0x75,0x74,0x20,0x2e,0x35,0x73,0x20,0x32,0x2e,0x35,0x73,0x3b,0x76,0x69,0x73,0x69,0x62,0x69,0x6c,0x69,0x74,0x79,0x3a,0x76,0x69,0x73,0x69,0x62,0x6c,0x65,0x7d,0x40,0x2d,0x77,0x65,0x62,0x6b,0x69,0x74,0x2d,0x6b,0x65,0x79,0x66,0x72,0x61,0x6d,0x65,0x73,0x20,0x66,0x61,0x64,0x65,0x69,0x6e,0x7b,0x66,0x72,0x6f,0x6d,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x32,0x30,0x25,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x30,0x7d,0x74,0x6f,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x33,0x30,0x25,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x2e,0x39,0x7d,0x7d,0x40,0x6b,0x65,0x79,0x66,0x72,0x61,0x6d,0x65,0x73,0x20,0x66,0x61,0x64,0x65,0x69,0x6e,0x7b,0x66,0x72,0x6f,0x6d,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x32,0x30,0x25,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x30,0x7d,0x74,0x6f,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x33,0x30,0x25,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x2e,0x39,0x7d,0x7d,0x40,0x2d,0x77,0x65,0x62,0x6b,0x69,0x74,0x2d,0x6b,0x65,0x79,0x66,0x72,0x61,0x6d,0x65,0x73,0x20,0x66,0x61,0x64,0x65,0x6f,0x75,0x74,0x7b,0x66,0x72,0x6f,0x6d,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x33,0x30,0x25,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x2e,0x39,0x7d,0x74,0x6f,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x30,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x30,0x7d,0x7d,0x40,0x6b,0x65,0x79,0x66,0x72,0x61,0x6d,0x65,0x73,0x20,0x66,0x61,0x64,0x65,0x6f,0x75,0x74,0x7b,0x66,0x72,0x6f,0x6d,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x33,0x30,0x25,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x2e,0x39,0x7d,0x74,0x6f,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x30,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x30,0x7d,0x7d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x30,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x31,0x46,0x31,0x46,0x31,0x7d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x31,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x43,0x46,0x46,0x39,0x35,0x7d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x32,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x39,0x44,0x43,0x45,0x46,0x45,0x7d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x33,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x41,0x34,0x46,0x43,0x37,0x39,0x7d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x34,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x32,0x41,0x42,0x33,0x39,0x7d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x39,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x35,0x30,0x7d,0x2e,0x6c,0x6f,0x67,0x76,0x69,0x65,0x77,0x65,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x32,0x37,0x32,0x37,0x32,0x37,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x31,0x46,0x31,0x46,0x31,0x3b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x35,0x33,0x30,0x70,0x78,0x3b,0x6f,0x76,0x65,0x72,0x66,0x6c,0x6f,0x77,0x3a,0x61,0x75,0x74,0x6f,0x7d,0x74,0x65,0x78,0x74,0x61,0x72,0x65,0x61,0x3a,0x68,0x6f,0x76,0x65,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x63,0x63,0x63,0x7d,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6d,0x75,0x6c,0x74,0x69,0x72,0x6f,0x77,0x20,0x74,0x68,0x2c,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x20,0x74,0x68,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x34,0x34,0x34,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x38,0x38,0x38,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x46,0x46,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x77,0x65,0x69,0x67,0x68,0x74,0x3a,0x37,0x30,0x30,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x36,0x70,0x78,0x3b,0x61,0x6c,0x69,0x67,0x6e,0x2d,0x63,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x3a,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x63,0x65,0x6e,0x74,0x65,0x72,0x7d,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6d,0x75,0x6c,0x74,0x69,0x72,0x6f,0x77,0x2c,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x63,0x6f,0x6c,0x6c,0x61,0x70,0x73,0x65,0x3a,0x63,0x6f,0x6c,0x6c,0x61,0x70,0x73,0x65,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x30,0x30,0x3b,0x6d,0x69,0x6e,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x34,0x32,0x30,0x70,0x78,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x30,0x30,0x25,0x7d,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6d,0x75,0x6c,0x74,0x69,0x72,0x6f,0x77,0x20,0x74,0x72,0x2c,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x20,0x74,0x64,0x2c,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x20,0x74,0x72,0x7b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x7d,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x20,0x74,0x64,0x7b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x33,0x30,0x70,0x78,0x7d,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6d,0x75,0x6c,0x74,0x69,0x72,0x6f,0x77,0x20,0x74,0x64,0x7b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x33,0x30,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x3b,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x63,0x65,0x6e,0x74,0x65,0x72,0x7d,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6d,0x75,0x6c,0x74,0x69,0x72,0x6f,0x77,0x20,0x74,0x72,0x3a,0x6e,0x74,0x68,0x2d,0x63,0x68,0x69,0x6c,0x64,0x28,0x65,0x76,0x65,0x6e,0x29,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x44,0x45,0x45,0x36,0x46,0x46,0x7d,0x2e,0x68,0x69,0x67,0x68,0x6c,0x69,0x67,0x68,0x74,0x20,0x74,0x64,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x64,0x62,0x66,0x66,0x30,0x30,0x37,0x35,0x7d,0x2e,0x61,0x70,0x68,0x65,0x61,0x64,0x65,0x72,0x2c,0x2e,0x68,0x65,0x61,0x64,0x65,0x72,0x6d,0x65,0x6e,0x75,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x38,0x46,0x38,0x46,0x38,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x38,0x70,0x78,0x20,0x31,0x32,0x70,0x78,0x7d,0x2e,0x6e,0x6f,0x74,0x65,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x34,0x34,0x34,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x74,0x79,0x6c,0x65,0x3a,0x69,0x74,0x61,0x6c,0x69,0x63,0x7d,0x2e,0x68,0x65,0x61,0x64,0x65,0x72,0x6d,0x65,0x6e,0x75,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x31,0x70,0x78,0x20,0x73,0x6f,0x6c,0x69,0x64,0x20,0x23,0x44,0x44,0x44,0x3b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x39,0x30,0x70,0x78,0x3b,0x6c,0x65,0x66,0x74,0x3a,0x30,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x66,0x69,0x78,0x65,0x64,0x3b,0x72,0x69,0x67,0x68,0x74,0x3a,0x30,0x3b,0x74,0x6f,0x70,0x3a,0x30,0x3b,0x7a,0x2d,0x69,0x6e,0x64,0x65,0x78,0x3a,0x31,0x7d,0x2e,0x62,0x6f,0x64,0x79,0x6d,0x65,0x6e,0x75,0x7b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x74,0x6f,0x70,0x3a,0x39,0x36,0x70,0x78,0x7d,0x2e,0x6d,0x65,0x6e,0x75,0x62,0x61,0x72,0x7b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x69,0x6e,0x68,0x65,0x72,0x69,0x74,0x3b,0x74,0x6f,0x70,0x3a,0x35,0x35,0x70,0x78,0x7d,0x2e,0x6d,0x65,0x6e,0x75,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x3a,0x73,0x6f,0x6c,0x69,0x64,0x20,0x74,0x72,0x61,0x6e,0x73,0x70,0x61,0x72,0x65,0x6e,0x74,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x34,0x70,0x78,0x20,0x34,0x70,0x78,0x20,0x30,0x20,0x30,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x34,0x70,0x78,0x20,0x31,0x70,0x78,0x20,0x31,0x70,0x78,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x34,0x34,0x34,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x20,0x31,0x36,0x70,0x78,0x20,0x38,0x70,0x78,0x3b,0x77,0x68,0x69,0x74,0x65,0x2d,0x73,0x70,0x61,0x63,0x65,0x3a,0x6e,0x6f,0x77,0x72,0x61,0x70,0x7d,0x2e,0x6d,0x65,0x6e,0x75,0x2e,0x61,0x63,0x74,0x69,0x76,0x65,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x46,0x46,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x37,0x44,0x20,0x23,0x44,0x44,0x44,0x20,0x23,0x46,0x46,0x46,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x30,0x30,0x7d,0x2e,0x6d,0x65,0x6e,0x75,0x3a,0x68,0x6f,0x76,0x65,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x3a,0x23,0x44,0x45,0x46,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x30,0x30,0x7d,0x2e,0x6d,0x65,0x6e,0x75,0x5f,0x62,0x75,0x74,0x74,0x6f,0x6e,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x6e,0x6f,0x6e,0x65,0x7d,0x2e,0x6f,0x6e,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x67,0x72,0x65,0x65,0x6e,0x7d,0x2e,0x6f,0x66,0x66,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x72,0x65,0x64,0x7d,0x2e,0x64,0x69,0x76,0x5f,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x38,0x30,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x34,0x70,0x78,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x32,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x31,0x70,0x78,0x20,0x31,0x30,0x70,0x78,0x7d,0x2e,0x61,0x6c,0x65,0x72,0x74,0x2c,0x2e,0x77,0x61,0x72,0x6e,0x69,0x6e,0x67,0x7b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x31,0x35,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x32,0x30,0x70,0x78,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x66,0x66,0x66,0x7d,0x2e,0x64,0x69,0x76,0x5f,0x62,0x72,0x7b,0x63,0x6c,0x65,0x61,0x72,0x3a,0x62,0x6f,0x74,0x68,0x7d,0x2e,0x61,0x6c,0x65,0x72,0x74,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x66,0x34,0x34,0x33,0x33,0x36,0x7d,0x2e,0x77,0x61,0x72,0x6e,0x69,0x6e,0x67,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x66,0x66,0x63,0x61,0x31,0x37,0x7d,0x2e,0x63,0x6c,0x6f,0x73,0x65,0x62,0x74,0x6e,0x7b,0x63,0x75,0x72,0x73,0x6f,0x72,0x3a,0x70,0x6f,0x69,0x6e,0x74,0x65,0x72,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x32,0x32,0x70,0x78,0x3b,0x6c,0x69,0x6e,0x65,0x2d,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x32,0x30,0x70,0x78,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x6c,0x65,0x66,0x74,0x3a,0x31,0x35,0x70,0x78,0x3b,0x74,0x72,0x61,0x6e,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x2e,0x33,0x73,0x7d,0x2e,0x63,0x6c,0x6f,0x73,0x65,0x62,0x74,0x6e,0x3a,0x68,0x6f,0x76,0x65,0x72,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x30,0x30,0x7d,0x73,0x65,0x63,0x74,0x69,0x6f,0x6e,0x7b,0x6f,0x76,0x65,0x72,0x66,0x6c,0x6f,0x77,0x2d,0x78,0x3a,0x61,0x75,0x74,0x6f,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x30,0x30,0x25,0x7d,0x40,0x6d,0x65,0x64,0x69,0x61,0x20,0x73,0x63,0x72,0x65,0x65,0x6e,0x20,0x61,0x6e,0x64,0x20,0x28,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x39,0x36,0x30,0x70,0x78,0x29,0x7b,0x2e,0x73,0x68,0x6f,0x77,0x6d,0x65,0x6e,0x75,0x6c,0x61,0x62,0x65,0x6c,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x6e,0x6f,0x6e,0x65,0x7d,0x2e,0x6d,0x65,0x6e,0x75,0x7b,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x31,0x76,0x77,0x3b,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x34,0x38,0x70,0x78,0x7d,0x7d,0x0a,0}; +static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = {0x2e,0x63,0x6c,0x6f,0x73,0x65,0x62,0x74,0x6e,0x2c,0x68,0x31,0x2c,0x68,0x32,0x2c,0x68,0x33,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x77,0x65,0x69,0x67,0x68,0x74,0x3a,0x37,0x30,0x30,0x7d,0x68,0x31,0x2c,0x68,0x36,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x37,0x44,0x7d,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x2c,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x7b,0x63,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x3a,0x27,0x27,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x2c,0x2e,0x6d,0x65,0x6e,0x75,0x7b,0x74,0x65,0x78,0x74,0x2d,0x64,0x65,0x63,0x6f,0x72,0x61,0x74,0x69,0x6f,0x6e,0x3a,0x6e,0x6f,0x6e,0x65,0x7d,0x2e,0x64,0x69,0x76,0x5f,0x6c,0x2c,0x2e,0x6d,0x65,0x6e,0x75,0x7b,0x66,0x6c,0x6f,0x61,0x74,0x3a,0x6c,0x65,0x66,0x74,0x7d,0x2e,0x63,0x6c,0x6f,0x73,0x65,0x62,0x74,0x6e,0x2c,0x2e,0x64,0x69,0x76,0x5f,0x72,0x7b,0x66,0x6c,0x6f,0x61,0x74,0x3a,0x72,0x69,0x67,0x68,0x74,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x66,0x66,0x66,0x7d,0x2a,0x7b,0x62,0x6f,0x78,0x2d,0x73,0x69,0x7a,0x69,0x6e,0x67,0x3a,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x62,0x6f,0x78,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x66,0x61,0x6d,0x69,0x6c,0x79,0x3a,0x73,0x61,0x6e,0x73,0x2d,0x73,0x65,0x72,0x69,0x66,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x32,0x70,0x74,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x30,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x30,0x7d,0x68,0x31,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x36,0x70,0x74,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x38,0x70,0x78,0x20,0x30,0x7d,0x68,0x32,0x2c,0x68,0x33,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x32,0x70,0x74,0x7d,0x68,0x32,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x34,0x34,0x34,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x46,0x46,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x30,0x20,0x2d,0x34,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x36,0x70,0x78,0x7d,0x68,0x33,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x45,0x45,0x45,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x34,0x34,0x34,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x31,0x36,0x70,0x78,0x20,0x2d,0x34,0x70,0x78,0x20,0x30,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x7d,0x68,0x36,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x30,0x70,0x74,0x7d,0x63,0x6f,0x64,0x65,0x2c,0x6b,0x62,0x64,0x2c,0x70,0x72,0x65,0x2c,0x73,0x61,0x6d,0x70,0x2c,0x74,0x74,0x2c,0x78,0x6d,0x70,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x66,0x61,0x6d,0x69,0x6c,0x79,0x3a,0x6d,0x6f,0x6e,0x6f,0x73,0x70,0x61,0x63,0x65,0x2c,0x6d,0x6f,0x6e,0x6f,0x73,0x70,0x61,0x63,0x65,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x65,0x6d,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x37,0x44,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x34,0x70,0x78,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x46,0x46,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x34,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x20,0x31,0x36,0x70,0x78,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x2e,0x68,0x65,0x6c,0x70,0x2c,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x2c,0x69,0x6e,0x70,0x75,0x74,0x2c,0x73,0x65,0x6c,0x65,0x63,0x74,0x2c,0x74,0x65,0x78,0x74,0x61,0x72,0x65,0x61,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x67,0x72,0x61,0x79,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x73,0x74,0x79,0x6c,0x65,0x3a,0x73,0x6f,0x6c,0x69,0x64,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x70,0x78,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x2e,0x6c,0x69,0x6e,0x6b,0x2e,0x77,0x69,0x64,0x65,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x69,0x6e,0x6c,0x69,0x6e,0x65,0x2d,0x62,0x6c,0x6f,0x63,0x6b,0x3b,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x30,0x30,0x25,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x2e,0x6c,0x69,0x6e,0x6b,0x2e,0x72,0x65,0x64,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x72,0x65,0x64,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x2e,0x68,0x65,0x6c,0x70,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x35,0x30,0x25,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x32,0x70,0x78,0x20,0x34,0x70,0x78,0x7d,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x2c,0x69,0x6e,0x70,0x75,0x74,0x2c,0x73,0x65,0x6c,0x65,0x63,0x74,0x2c,0x74,0x65,0x78,0x74,0x61,0x72,0x65,0x61,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x34,0x70,0x78,0x7d,0x2e,0x62,0x75,0x74,0x74,0x6f,0x6e,0x3a,0x68,0x6f,0x76,0x65,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x3a,0x23,0x33,0x36,0x39,0x7d,0x69,0x6e,0x70,0x75,0x74,0x3a,0x68,0x6f,0x76,0x65,0x72,0x2c,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x68,0x6f,0x76,0x65,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x63,0x63,0x63,0x7d,0x69,0x6e,0x70,0x75,0x74,0x2c,0x73,0x65,0x6c,0x65,0x63,0x74,0x2c,0x74,0x65,0x78,0x74,0x61,0x72,0x65,0x61,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x65,0x65,0x65,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x34,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x20,0x38,0x70,0x78,0x7d,0x69,0x6e,0x70,0x75,0x74,0x2e,0x77,0x69,0x64,0x65,0x2c,0x73,0x65,0x6c,0x65,0x63,0x74,0x2e,0x77,0x69,0x64,0x65,0x7b,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x35,0x30,0x30,0x70,0x78,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x38,0x30,0x25,0x7d,0x69,0x6e,0x70,0x75,0x74,0x2e,0x78,0x77,0x69,0x64,0x65,0x2c,0x73,0x65,0x6c,0x65,0x63,0x74,0x2e,0x78,0x77,0x69,0x64,0x65,0x7b,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x35,0x30,0x30,0x70,0x78,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x30,0x30,0x25,0x7d,0x2e,0x77,0x69,0x64,0x65,0x6e,0x75,0x6d,0x62,0x65,0x72,0x7b,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x35,0x30,0x30,0x70,0x78,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x37,0x65,0x6d,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x2c,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x32,0x70,0x74,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x2d,0x6c,0x65,0x66,0x74,0x3a,0x33,0x35,0x70,0x78,0x3b,0x63,0x75,0x72,0x73,0x6f,0x72,0x3a,0x70,0x6f,0x69,0x6e,0x74,0x65,0x72,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x7b,0x2d,0x6d,0x6f,0x7a,0x2d,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x2d,0x6d,0x73,0x2d,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x2d,0x77,0x65,0x62,0x6b,0x69,0x74,0x2d,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x62,0x6c,0x6f,0x63,0x6b,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x6c,0x65,0x66,0x74,0x3a,0x34,0x70,0x78,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x74,0x6f,0x70,0x3a,0x30,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x72,0x65,0x6c,0x61,0x74,0x69,0x76,0x65,0x3b,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x20,0x69,0x6e,0x70,0x75,0x74,0x7b,0x63,0x75,0x72,0x73,0x6f,0x72,0x3a,0x70,0x6f,0x69,0x6e,0x74,0x65,0x72,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x30,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x7d,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x2e,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x64,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x67,0x72,0x65,0x79,0x7d,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x65,0x65,0x65,0x3b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x32,0x35,0x70,0x78,0x3b,0x6c,0x65,0x66,0x74,0x3a,0x30,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x3b,0x74,0x6f,0x70,0x3a,0x30,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x32,0x35,0x70,0x78,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x3a,0x68,0x6f,0x76,0x65,0x72,0x20,0x69,0x6e,0x70,0x75,0x74,0x7e,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x63,0x63,0x63,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x20,0x69,0x6e,0x70,0x75,0x74,0x3a,0x63,0x68,0x65,0x63,0x6b,0x65,0x64,0x7e,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x37,0x44,0x7d,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x20,0x69,0x6e,0x70,0x75,0x74,0x3a,0x63,0x68,0x65,0x63,0x6b,0x65,0x64,0x7e,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x2c,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x62,0x6c,0x6f,0x63,0x6b,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x20,0x2e,0x63,0x68,0x65,0x63,0x6b,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x7b,0x2d,0x6d,0x73,0x2d,0x74,0x72,0x61,0x6e,0x73,0x66,0x6f,0x72,0x6d,0x3a,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x34,0x35,0x64,0x65,0x67,0x29,0x3b,0x2d,0x77,0x65,0x62,0x6b,0x69,0x74,0x2d,0x74,0x72,0x61,0x6e,0x73,0x66,0x6f,0x72,0x6d,0x3a,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x34,0x35,0x64,0x65,0x67,0x29,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x3a,0x73,0x6f,0x6c,0x69,0x64,0x20,0x23,0x66,0x66,0x66,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x30,0x20,0x33,0x70,0x78,0x20,0x33,0x70,0x78,0x20,0x30,0x3b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x31,0x30,0x70,0x78,0x3b,0x6c,0x65,0x66,0x74,0x3a,0x37,0x70,0x78,0x3b,0x74,0x6f,0x70,0x3a,0x33,0x70,0x78,0x3b,0x74,0x72,0x61,0x6e,0x73,0x66,0x6f,0x72,0x6d,0x3a,0x72,0x6f,0x74,0x61,0x74,0x65,0x28,0x34,0x35,0x64,0x65,0x67,0x29,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x35,0x70,0x78,0x7d,0x23,0x74,0x6f,0x61,0x73,0x74,0x6d,0x65,0x73,0x73,0x61,0x67,0x65,0x2c,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x2c,0x2e,0x6c,0x6f,0x67,0x76,0x69,0x65,0x77,0x65,0x72,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x67,0x72,0x61,0x79,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x73,0x74,0x79,0x6c,0x65,0x3a,0x73,0x6f,0x6c,0x69,0x64,0x7d,0x23,0x74,0x6f,0x61,0x73,0x74,0x6d,0x65,0x73,0x73,0x61,0x67,0x65,0x2c,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x70,0x78,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x7b,0x2d,0x6d,0x6f,0x7a,0x2d,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x2d,0x6d,0x73,0x2d,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x2d,0x77,0x65,0x62,0x6b,0x69,0x74,0x2d,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x32,0x30,0x70,0x78,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x6c,0x65,0x66,0x74,0x3a,0x39,0x70,0x78,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x72,0x65,0x6c,0x61,0x74,0x69,0x76,0x65,0x3b,0x75,0x73,0x65,0x72,0x2d,0x73,0x65,0x6c,0x65,0x63,0x74,0x3a,0x6e,0x6f,0x6e,0x65,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x7b,0x63,0x75,0x72,0x73,0x6f,0x72,0x3a,0x70,0x6f,0x69,0x6e,0x74,0x65,0x72,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x30,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x7d,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x65,0x65,0x65,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x35,0x30,0x25,0x3b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x32,0x36,0x70,0x78,0x3b,0x6c,0x65,0x66,0x74,0x3a,0x30,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x3b,0x74,0x6f,0x70,0x3a,0x30,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x32,0x36,0x70,0x78,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x3a,0x68,0x6f,0x76,0x65,0x72,0x20,0x69,0x6e,0x70,0x75,0x74,0x7e,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x63,0x63,0x63,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x3a,0x63,0x68,0x65,0x63,0x6b,0x65,0x64,0x7e,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x37,0x44,0x7d,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x6e,0x6f,0x6e,0x65,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x61,0x62,0x73,0x6f,0x6c,0x75,0x74,0x65,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x20,0x69,0x6e,0x70,0x75,0x74,0x3a,0x63,0x68,0x65,0x63,0x6b,0x65,0x64,0x7e,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x62,0x6c,0x6f,0x63,0x6b,0x7d,0x2e,0x63,0x6f,0x6e,0x74,0x61,0x69,0x6e,0x65,0x72,0x32,0x20,0x2e,0x64,0x6f,0x74,0x6d,0x61,0x72,0x6b,0x3a,0x61,0x66,0x74,0x65,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x3a,0x23,0x66,0x66,0x66,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x35,0x30,0x25,0x3b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x38,0x70,0x78,0x3b,0x6c,0x65,0x66,0x74,0x3a,0x38,0x70,0x78,0x3b,0x74,0x6f,0x70,0x3a,0x38,0x70,0x78,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x38,0x70,0x78,0x7d,0x2e,0x6c,0x6f,0x67,0x76,0x69,0x65,0x77,0x65,0x72,0x2c,0x74,0x65,0x78,0x74,0x61,0x72,0x65,0x61,0x7b,0x66,0x6f,0x6e,0x74,0x2d,0x66,0x61,0x6d,0x69,0x6c,0x79,0x3a,0x27,0x4c,0x75,0x63,0x69,0x64,0x61,0x20,0x43,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x27,0x2c,0x4d,0x6f,0x6e,0x61,0x63,0x6f,0x2c,0x6d,0x6f,0x6e,0x6f,0x73,0x70,0x61,0x63,0x65,0x3b,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x30,0x30,0x30,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x20,0x38,0x70,0x78,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x38,0x30,0x25,0x7d,0x23,0x74,0x6f,0x61,0x73,0x74,0x6d,0x65,0x73,0x73,0x61,0x67,0x65,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x37,0x44,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x34,0x70,0x78,0x3b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x33,0x30,0x25,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x66,0x66,0x66,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x31,0x37,0x70,0x78,0x3b,0x6c,0x65,0x66,0x74,0x3a,0x32,0x38,0x32,0x70,0x78,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x6c,0x65,0x66,0x74,0x3a,0x2d,0x31,0x32,0x35,0x70,0x78,0x3b,0x6d,0x69,0x6e,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x32,0x35,0x30,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x31,0x36,0x70,0x78,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x66,0x69,0x78,0x65,0x64,0x3b,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x76,0x69,0x73,0x69,0x62,0x69,0x6c,0x69,0x74,0x79,0x3a,0x68,0x69,0x64,0x64,0x65,0x6e,0x3b,0x7a,0x2d,0x69,0x6e,0x64,0x65,0x78,0x3a,0x31,0x7d,0x23,0x74,0x6f,0x61,0x73,0x74,0x6d,0x65,0x73,0x73,0x61,0x67,0x65,0x2e,0x73,0x68,0x6f,0x77,0x7b,0x2d,0x77,0x65,0x62,0x6b,0x69,0x74,0x2d,0x61,0x6e,0x69,0x6d,0x61,0x74,0x69,0x6f,0x6e,0x3a,0x66,0x61,0x64,0x65,0x69,0x6e,0x20,0x2e,0x35,0x73,0x2c,0x66,0x61,0x64,0x65,0x6f,0x75,0x74,0x20,0x2e,0x35,0x73,0x20,0x32,0x2e,0x35,0x73,0x3b,0x61,0x6e,0x69,0x6d,0x61,0x74,0x69,0x6f,0x6e,0x3a,0x66,0x61,0x64,0x65,0x69,0x6e,0x20,0x2e,0x35,0x73,0x2c,0x66,0x61,0x64,0x65,0x6f,0x75,0x74,0x20,0x2e,0x35,0x73,0x20,0x32,0x2e,0x35,0x73,0x3b,0x76,0x69,0x73,0x69,0x62,0x69,0x6c,0x69,0x74,0x79,0x3a,0x76,0x69,0x73,0x69,0x62,0x6c,0x65,0x7d,0x40,0x2d,0x77,0x65,0x62,0x6b,0x69,0x74,0x2d,0x6b,0x65,0x79,0x66,0x72,0x61,0x6d,0x65,0x73,0x20,0x66,0x61,0x64,0x65,0x69,0x6e,0x7b,0x66,0x72,0x6f,0x6d,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x32,0x30,0x25,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x30,0x7d,0x74,0x6f,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x33,0x30,0x25,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x2e,0x39,0x7d,0x7d,0x40,0x6b,0x65,0x79,0x66,0x72,0x61,0x6d,0x65,0x73,0x20,0x66,0x61,0x64,0x65,0x69,0x6e,0x7b,0x66,0x72,0x6f,0x6d,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x32,0x30,0x25,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x30,0x7d,0x74,0x6f,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x33,0x30,0x25,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x2e,0x39,0x7d,0x7d,0x40,0x2d,0x77,0x65,0x62,0x6b,0x69,0x74,0x2d,0x6b,0x65,0x79,0x66,0x72,0x61,0x6d,0x65,0x73,0x20,0x66,0x61,0x64,0x65,0x6f,0x75,0x74,0x7b,0x66,0x72,0x6f,0x6d,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x33,0x30,0x25,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x2e,0x39,0x7d,0x74,0x6f,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x30,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x30,0x7d,0x7d,0x40,0x6b,0x65,0x79,0x66,0x72,0x61,0x6d,0x65,0x73,0x20,0x66,0x61,0x64,0x65,0x6f,0x75,0x74,0x7b,0x66,0x72,0x6f,0x6d,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x33,0x30,0x25,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x2e,0x39,0x7d,0x74,0x6f,0x7b,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x30,0x3b,0x6f,0x70,0x61,0x63,0x69,0x74,0x79,0x3a,0x30,0x7d,0x7d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x30,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x31,0x46,0x31,0x46,0x31,0x7d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x31,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x43,0x46,0x46,0x39,0x35,0x7d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x32,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x39,0x44,0x43,0x45,0x46,0x45,0x7d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x33,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x41,0x34,0x46,0x43,0x37,0x39,0x7d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x34,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x32,0x41,0x42,0x33,0x39,0x7d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x39,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x35,0x30,0x7d,0x2e,0x6c,0x6f,0x67,0x76,0x69,0x65,0x77,0x65,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x32,0x37,0x32,0x37,0x32,0x37,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x31,0x46,0x31,0x46,0x31,0x3b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x35,0x33,0x30,0x70,0x78,0x3b,0x6f,0x76,0x65,0x72,0x66,0x6c,0x6f,0x77,0x3a,0x61,0x75,0x74,0x6f,0x7d,0x74,0x65,0x78,0x74,0x61,0x72,0x65,0x61,0x3a,0x68,0x6f,0x76,0x65,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x63,0x63,0x63,0x7d,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6d,0x75,0x6c,0x74,0x69,0x72,0x6f,0x77,0x20,0x74,0x68,0x2c,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x20,0x74,0x68,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x34,0x34,0x34,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x38,0x38,0x38,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x46,0x46,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x77,0x65,0x69,0x67,0x68,0x74,0x3a,0x37,0x30,0x30,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x36,0x70,0x78,0x3b,0x61,0x6c,0x69,0x67,0x6e,0x2d,0x63,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x3a,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x63,0x65,0x6e,0x74,0x65,0x72,0x7d,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6d,0x75,0x6c,0x74,0x69,0x72,0x6f,0x77,0x2c,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x63,0x6f,0x6c,0x6c,0x61,0x70,0x73,0x65,0x3a,0x63,0x6f,0x6c,0x6c,0x61,0x70,0x73,0x65,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x30,0x30,0x3b,0x6d,0x69,0x6e,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x34,0x32,0x30,0x70,0x78,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x30,0x30,0x25,0x7d,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6d,0x75,0x6c,0x74,0x69,0x72,0x6f,0x77,0x20,0x74,0x72,0x2c,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x20,0x74,0x64,0x2c,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x20,0x74,0x72,0x7b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x7d,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x20,0x74,0x64,0x7b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x33,0x30,0x70,0x78,0x7d,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6d,0x75,0x6c,0x74,0x69,0x72,0x6f,0x77,0x20,0x74,0x64,0x7b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x33,0x30,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x3b,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67,0x6e,0x3a,0x63,0x65,0x6e,0x74,0x65,0x72,0x7d,0x74,0x61,0x62,0x6c,0x65,0x2e,0x6d,0x75,0x6c,0x74,0x69,0x72,0x6f,0x77,0x20,0x74,0x72,0x3a,0x6e,0x74,0x68,0x2d,0x63,0x68,0x69,0x6c,0x64,0x28,0x65,0x76,0x65,0x6e,0x29,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x44,0x45,0x45,0x36,0x46,0x46,0x7d,0x2e,0x68,0x69,0x67,0x68,0x6c,0x69,0x67,0x68,0x74,0x20,0x74,0x64,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x64,0x62,0x66,0x66,0x30,0x30,0x37,0x35,0x7d,0x2e,0x61,0x70,0x68,0x65,0x61,0x64,0x65,0x72,0x2c,0x2e,0x68,0x65,0x61,0x64,0x65,0x72,0x6d,0x65,0x6e,0x75,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x38,0x46,0x38,0x46,0x38,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x38,0x70,0x78,0x20,0x31,0x32,0x70,0x78,0x7d,0x2e,0x6e,0x6f,0x74,0x65,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x34,0x34,0x34,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x74,0x79,0x6c,0x65,0x3a,0x69,0x74,0x61,0x6c,0x69,0x63,0x7d,0x2e,0x68,0x65,0x61,0x64,0x65,0x72,0x6d,0x65,0x6e,0x75,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x31,0x70,0x78,0x20,0x73,0x6f,0x6c,0x69,0x64,0x20,0x23,0x44,0x44,0x44,0x3b,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x39,0x30,0x70,0x78,0x3b,0x6c,0x65,0x66,0x74,0x3a,0x30,0x3b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x66,0x69,0x78,0x65,0x64,0x3b,0x72,0x69,0x67,0x68,0x74,0x3a,0x30,0x3b,0x74,0x6f,0x70,0x3a,0x30,0x3b,0x7a,0x2d,0x69,0x6e,0x64,0x65,0x78,0x3a,0x31,0x7d,0x2e,0x62,0x6f,0x64,0x79,0x6d,0x65,0x6e,0x75,0x7b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x74,0x6f,0x70,0x3a,0x39,0x36,0x70,0x78,0x7d,0x2e,0x6d,0x65,0x6e,0x75,0x62,0x61,0x72,0x7b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x69,0x6e,0x68,0x65,0x72,0x69,0x74,0x3b,0x74,0x6f,0x70,0x3a,0x35,0x35,0x70,0x78,0x7d,0x2e,0x6d,0x65,0x6e,0x75,0x7b,0x62,0x6f,0x72,0x64,0x65,0x72,0x3a,0x73,0x6f,0x6c,0x69,0x64,0x20,0x74,0x72,0x61,0x6e,0x73,0x70,0x61,0x72,0x65,0x6e,0x74,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x34,0x70,0x78,0x20,0x34,0x70,0x78,0x20,0x30,0x20,0x30,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x34,0x70,0x78,0x20,0x31,0x70,0x78,0x20,0x31,0x70,0x78,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x34,0x34,0x34,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x34,0x70,0x78,0x20,0x31,0x36,0x70,0x78,0x20,0x38,0x70,0x78,0x3b,0x77,0x68,0x69,0x74,0x65,0x2d,0x73,0x70,0x61,0x63,0x65,0x3a,0x6e,0x6f,0x77,0x72,0x61,0x70,0x7d,0x2e,0x6d,0x65,0x6e,0x75,0x2e,0x61,0x63,0x74,0x69,0x76,0x65,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x46,0x46,0x46,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x37,0x44,0x20,0x23,0x44,0x44,0x44,0x20,0x23,0x46,0x46,0x46,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x30,0x30,0x7d,0x2e,0x6d,0x65,0x6e,0x75,0x3a,0x68,0x6f,0x76,0x65,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x3a,0x23,0x44,0x45,0x46,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x30,0x30,0x7d,0x2e,0x6d,0x65,0x6e,0x75,0x5f,0x62,0x75,0x74,0x74,0x6f,0x6e,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x6e,0x6f,0x6e,0x65,0x7d,0x2e,0x6f,0x6e,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x67,0x72,0x65,0x65,0x6e,0x7d,0x2e,0x6f,0x66,0x66,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x72,0x65,0x64,0x7d,0x2e,0x64,0x69,0x76,0x5f,0x72,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x38,0x30,0x3b,0x62,0x6f,0x72,0x64,0x65,0x72,0x2d,0x72,0x61,0x64,0x69,0x75,0x73,0x3a,0x34,0x70,0x78,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x3a,0x32,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x31,0x70,0x78,0x20,0x31,0x30,0x70,0x78,0x7d,0x2e,0x61,0x6c,0x65,0x72,0x74,0x2c,0x2e,0x77,0x61,0x72,0x6e,0x69,0x6e,0x67,0x7b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x62,0x6f,0x74,0x74,0x6f,0x6d,0x3a,0x31,0x35,0x70,0x78,0x3b,0x70,0x61,0x64,0x64,0x69,0x6e,0x67,0x3a,0x32,0x30,0x70,0x78,0x3b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x66,0x66,0x66,0x7d,0x2e,0x64,0x69,0x76,0x5f,0x62,0x72,0x7b,0x63,0x6c,0x65,0x61,0x72,0x3a,0x62,0x6f,0x74,0x68,0x7d,0x2e,0x61,0x6c,0x65,0x72,0x74,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x66,0x34,0x34,0x33,0x33,0x36,0x7d,0x2e,0x77,0x61,0x72,0x6e,0x69,0x6e,0x67,0x7b,0x62,0x61,0x63,0x6b,0x67,0x72,0x6f,0x75,0x6e,0x64,0x2d,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x66,0x66,0x63,0x61,0x31,0x37,0x7d,0x2e,0x63,0x6c,0x6f,0x73,0x65,0x62,0x74,0x6e,0x7b,0x63,0x75,0x72,0x73,0x6f,0x72,0x3a,0x70,0x6f,0x69,0x6e,0x74,0x65,0x72,0x3b,0x66,0x6f,0x6e,0x74,0x2d,0x73,0x69,0x7a,0x65,0x3a,0x32,0x32,0x70,0x78,0x3b,0x6c,0x69,0x6e,0x65,0x2d,0x68,0x65,0x69,0x67,0x68,0x74,0x3a,0x32,0x30,0x70,0x78,0x3b,0x6d,0x61,0x72,0x67,0x69,0x6e,0x2d,0x6c,0x65,0x66,0x74,0x3a,0x31,0x35,0x70,0x78,0x3b,0x74,0x72,0x61,0x6e,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3a,0x2e,0x33,0x73,0x7d,0x2e,0x63,0x6c,0x6f,0x73,0x65,0x62,0x74,0x6e,0x3a,0x68,0x6f,0x76,0x65,0x72,0x7b,0x63,0x6f,0x6c,0x6f,0x72,0x3a,0x23,0x30,0x30,0x30,0x7d,0x73,0x65,0x63,0x74,0x69,0x6f,0x6e,0x7b,0x6f,0x76,0x65,0x72,0x66,0x6c,0x6f,0x77,0x2d,0x78,0x3a,0x61,0x75,0x74,0x6f,0x3b,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x30,0x30,0x25,0x7d,0x40,0x6d,0x65,0x64,0x69,0x61,0x20,0x73,0x63,0x72,0x65,0x65,0x6e,0x20,0x61,0x6e,0x64,0x20,0x28,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x39,0x36,0x30,0x70,0x78,0x29,0x7b,0x2e,0x73,0x68,0x6f,0x77,0x6d,0x65,0x6e,0x75,0x6c,0x61,0x62,0x65,0x6c,0x7b,0x64,0x69,0x73,0x70,0x6c,0x61,0x79,0x3a,0x6e,0x6f,0x6e,0x65,0x7d,0x2e,0x6d,0x65,0x6e,0x75,0x7b,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x31,0x31,0x76,0x77,0x3b,0x6d,0x61,0x78,0x2d,0x77,0x69,0x64,0x74,0x68,0x3a,0x34,0x38,0x70,0x78,0x7d,0x7d,0x0a,0}; #endif // WEBSERVER_CSS // JavaScript blobs diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 620c92808..02e6635d3 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -619,7 +619,7 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) switch (Device[DeviceIndex].Type) { case DEVICE_TYPE_I2C: { - format_I2C_pin_description(); + format_I2C_pin_description(x); break; } case DEVICE_TYPE_SPI3: @@ -636,11 +636,11 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) case DEVICE_TYPE_ANALOG: { # ifdef ESP8266 - # if FEATURE_ADC_VCC + # if FEATURE_ADC_VCC addHtml(F("ADC (VDD)")); - # else // if FEATURE_ADC_VCC + # else // if FEATURE_ADC_VCC addHtml(F("ADC (TOUT)")); - # endif // if FEATURE_ADC_VCC + # endif // if FEATURE_ADC_VCC # endif // ifdef ESP8266 # ifdef ESP32 showpin1 = true; @@ -815,11 +815,13 @@ void format_SPI_port_description(int8_t spi_gpios[3]) # endif // ifdef ESP8266 } -void format_I2C_pin_description() +void format_I2C_pin_description(taskIndex_t x) { - Label_Gpio_toHtml(F("SDA"), formatGpioLabel(Settings.Pin_i2c_sda, false)); - html_BR(); - Label_Gpio_toHtml(F("SCL"), formatGpioLabel(Settings.Pin_i2c_scl, false)); + if (checkI2CConfigValid_toHtml(x)) { + Label_Gpio_toHtml(F("SDA"), formatGpioLabel(Settings.Pin_i2c_sda, false)); + html_BR(); + Label_Gpio_toHtml(F("SCL"), formatGpioLabel(Settings.Pin_i2c_scl, false)); + } } void format_SPI_pin_description(int8_t spi_gpios[3], taskIndex_t x) @@ -1040,11 +1042,6 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex addFormNote(F("SPI Interface is not configured yet (Hardware page).")); } - if ((Device[DeviceIndex].Type == DEVICE_TYPE_I2C) - && !Settings.isI2CEnabled()) { - addFormNote(F("I2C Interface is not configured yet (Hardware page).")); - } - if (Device[DeviceIndex].connectedToGPIOpins()) { // get descriptive GPIO-names from plugin struct EventStruct TempEvent(taskIndex); @@ -1104,6 +1101,11 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex) struct EventStruct TempEvent(taskIndex); addFormSubHeader(F("I2C options")); + + if (!Settings.isI2CEnabled()) { + addFormNote(F("I2C Interface is not configured yet (Hardware page).")); + } + String dummy; PluginCall(PLUGIN_WEBFORM_SHOW_I2C_PARAMS, &TempEvent, dummy); diff --git a/src/src/WebServer/DevicesPage.h b/src/src/WebServer/DevicesPage.h index 697941ddb..33710041b 100644 --- a/src/src/WebServer/DevicesPage.h +++ b/src/src/WebServer/DevicesPage.h @@ -14,6 +14,7 @@ #include "../Static/WebStaticData.h" #include "../Helpers/_Plugin_SensorTypeHelper.h" +#include "../Helpers/I2C_Plugin_Helper.h" #include "../Helpers/StringGenerator_GPIO.h" @@ -43,7 +44,7 @@ void format_I2C_port_description(taskIndex_t x); void format_SPI_port_description(int8_t spi_gpios[3]); -void format_I2C_pin_description(); +void format_I2C_pin_description(taskIndex_t x); void format_SPI_pin_description(int8_t spi_gpios[3], taskIndex_t x); diff --git a/src/src/WebServer/FileList.cpp b/src/src/WebServer/FileList.cpp index 3541542c4..23e7ac01f 100644 --- a/src/src/WebServer/FileList.cpp +++ b/src/src/WebServer/FileList.cpp @@ -401,7 +401,7 @@ void handle_SDfilelist() { while (entry) { html_TR_TD(); - size_t entrynameLength = strlen(entry.name()); + // size_t entrynameLength = strlen(entry.name()); if (entry.isDirectory()) { char SDcardChildDir[80]; diff --git a/src/src/WebServer/HTML_wrappers.cpp b/src/src/WebServer/HTML_wrappers.cpp index 97471bd36..6db6b7d3a 100644 --- a/src/src/WebServer/HTML_wrappers.cpp +++ b/src/src/WebServer/HTML_wrappers.cpp @@ -83,6 +83,12 @@ void html_TD() { addHtml(F("")); } +void html_TD(const String& style) { + addHtml(F("")); +} + void html_TD(int td_cnt) { for (int i = 0; i < td_cnt; ++i) { html_TD(); diff --git a/src/src/WebServer/HTML_wrappers.h b/src/src/WebServer/HTML_wrappers.h index e6cc24838..162423608 100644 --- a/src/src/WebServer/HTML_wrappers.h +++ b/src/src/WebServer/HTML_wrappers.h @@ -30,6 +30,8 @@ void html_TR_TD_height(int height); void html_TD(); +void html_TD(const String& style); + void html_TD(int td_cnt); extern int copyTextCounter; diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index c57e14338..1db17b82f 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -121,19 +121,23 @@ void handle_hardware() { addFormSubHeader(F("I2C Multiplexer")); // Select the type of multiplexer to use { - const __FlashStringHelper * i2c_muxtype_options[5]; - int i2c_muxtype_choices[5]; - i2c_muxtype_options[0] = F("- None -"); - i2c_muxtype_choices[0] = -1; - i2c_muxtype_options[1] = F("TCA9548a - 8 channel"); - i2c_muxtype_choices[1] = I2C_MULTIPLEXER_TCA9548A; - i2c_muxtype_options[2] = F("TCA9546a - 4 channel"); - i2c_muxtype_choices[2] = I2C_MULTIPLEXER_TCA9546A; - i2c_muxtype_options[3] = F("TCA9543a - 2 channel"); - i2c_muxtype_choices[3] = I2C_MULTIPLEXER_TCA9543A; - i2c_muxtype_options[4] = F("PCA9540 - 2 channel (experimental)"); - i2c_muxtype_choices[4] = I2C_MULTIPLEXER_PCA9540; - addFormSelector(F("I2C Multiplexer type"), F("pi2cmuxtype"), 5, i2c_muxtype_options, i2c_muxtype_choices, Settings.I2C_Multiplexer_Type); + # define I2C_MULTIPLEXER_OPTIONCOUNT 5 // Nr. of supported devices + 'None' + const __FlashStringHelper *i2c_muxtype_options[] = { + F("- None -"), + F("TCA9548a - 8 channel"), + F("TCA9546a - 4 channel"), + F("TCA9543a - 2 channel"), + F("PCA9540 - 2 channel (experimental)") + }; + const int i2c_muxtype_choices[] = { + -1, + I2C_MULTIPLEXER_TCA9548A, + I2C_MULTIPLEXER_TCA9546A, + I2C_MULTIPLEXER_TCA9543A, + I2C_MULTIPLEXER_PCA9540 + }; + addFormSelector(F("I2C Multiplexer type"), F("pi2cmuxtype"), I2C_MULTIPLEXER_OPTIONCOUNT, + i2c_muxtype_options, i2c_muxtype_choices, Settings.I2C_Multiplexer_Type); } // Select the I2C address for a port multiplexer { diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp index 5a37bddb2..a720cbbfb 100644 --- a/src/src/WebServer/JSON.cpp +++ b/src/src/WebServer/JSON.cpp @@ -154,6 +154,8 @@ void handle_json() LabelType::SYSTEM_LIBRARIES, LabelType::PLUGIN_COUNT, LabelType::PLUGIN_DESCRIPTION, + LabelType::BUILD_TIME, + LabelType::BINARY_FILENAME, LabelType::LOCAL_TIME, LabelType::TIME_SOURCE, LabelType::TIME_WANDER, @@ -198,6 +200,12 @@ void handle_json() LabelType::TIMEZONE_OFFSET, LabelType::LATITUDE, LabelType::LONGITUDE, + LabelType::SYSLOG_LOG_LEVEL, + LabelType::SERIAL_LOG_LEVEL, + LabelType::WEB_LOG_LEVEL, + #if FEATURE_SD + LabelType::SD_LOG_LEVEL, + #endif // if FEATURE_SD LabelType::MAX_LABEL diff --git a/static/espeasy_default.css b/static/espeasy_default.css index c2466b000..5f0ff50bf 100644 --- a/static/espeasy_default.css +++ b/static/espeasy_default.css @@ -141,6 +141,12 @@ select.wide { width: 80% } +input.xwide, +select.xwide { + max-width: 500px; + width: 100% +} + .widenumber { max-width: 500px; width: 100px diff --git a/static/espeasy_default.min.css b/static/espeasy_default.min.css index 220aa09e1..cbc62e329 100644 --- a/static/espeasy_default.min.css +++ b/static/espeasy_default.min.css @@ -1 +1 @@ -.closebtn,h1,h2,h3{font-weight:700}h1,h6{color:#07D}.checkmark:after,.dotmark:after{content:''}.button,.menu{text-decoration:none}.div_l,.menu{float:left}.closebtn,.div_r{float:right;color:#fff}*{box-sizing:border-box;font-family:sans-serif;font-size:12pt;margin:0;padding:0}h1{font-size:16pt;margin:8px 0}h2,h3{font-size:12pt}h2{background-color:#444;color:#FFF;margin:0 -4px;padding:6px}h3{background-color:#EEE;color:#444;margin:16px -4px 0;padding:4px}h6{font-size:10pt}code,kbd,pre,samp,tt,xmp{font-family:monospace,monospace;font-size:1em}.button{background-color:#07D;border:none;border-radius:4px;color:#FFF;margin:4px;padding:4px 16px}.button.help,.checkmark,input,select,textarea{border-color:gray;border-style:solid;border-width:1px}.button.link.wide{display:inline-block;text-align:center;width:100%}.button.link.red{background-color:red}.button.help{border-radius:50%;padding:2px 4px}.checkmark,input,select,textarea{border-radius:4px}.button:hover{background:#369}input:hover,select:hover{background-color:#ccc}input,select,textarea{background-color:#eee;margin:4px;padding:4px 8px}input.wide,select.wide{max-width:500px;width:80%}.widenumber{max-width:500px;width:7em}.container,.container2{font-size:12pt;padding-left:35px;cursor:pointer}.container{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;display:block;margin-left:4px;margin-top:0;position:relative;user-select:none}.container input{cursor:pointer;opacity:0;position:absolute}.checkmark.disabled{background-color:grey}.checkmark{background-color:#eee;height:25px;left:0;position:absolute;top:0;width:25px}.container:hover input~.checkmark{background-color:#ccc}.container input:checked~.checkmark{background-color:#07D}.checkmark:after{display:none;position:absolute}.container input:checked~.checkmark:after,.container2{display:block}.container .checkmark:after{-ms-transform:rotate(45deg);-webkit-transform:rotate(45deg);border:solid #fff;border-width:0 3px 3px 0;height:10px;left:7px;top:3px;transform:rotate(45deg);width:5px}#toastmessage,.dotmark,.logviewer{border-color:gray;border-style:solid}#toastmessage,.dotmark{border-width:1px}.container2{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;margin-bottom:20px;margin-left:9px;position:relative;user-select:none}.container2 input{cursor:pointer;opacity:0;position:absolute}.dotmark{background-color:#eee;border-radius:50%;height:26px;left:0;position:absolute;top:0;width:26px}.container2:hover input~.dotmark{background-color:#ccc}.container2 input:checked~.dotmark{background-color:#07D}.dotmark:after{display:none;position:absolute}.container2 input:checked~.dotmark:after{display:block}.container2 .dotmark:after{background:#fff;border-radius:50%;height:8px;left:8px;top:8px;width:8px}.logviewer,textarea{font-family:'Lucida Console',Monaco,monospace;max-width:1000px;padding:4px 8px;width:80%}#toastmessage{background-color:#07D;border-radius:4px;bottom:30%;color:#fff;font-size:17px;left:282px;margin-left:-125px;min-width:250px;padding:16px;position:fixed;text-align:center;visibility:hidden;z-index:1}#toastmessage.show{-webkit-animation:fadein .5s,fadeout .5s 2.5s;animation:fadein .5s,fadeout .5s 2.5s;visibility:visible}@-webkit-keyframes fadein{from{bottom:20%;opacity:0}to{bottom:30%;opacity:.9}}@keyframes fadein{from{bottom:20%;opacity:0}to{bottom:30%;opacity:.9}}@-webkit-keyframes fadeout{from{bottom:30%;opacity:.9}to{bottom:0;opacity:0}}@keyframes fadeout{from{bottom:30%;opacity:.9}to{bottom:0;opacity:0}}.level_0{color:#F1F1F1}.level_1{color:#FCFF95}.level_2{color:#9DCEFE}.level_3{color:#A4FC79}.level_4{color:#F2AB39}.level_9{color:#F50}.logviewer{background-color:#272727;color:#F1F1F1;height:530px;overflow:auto}textarea:hover{background-color:#ccc}table.multirow th,table.normal th{background-color:#444;border-color:#888;color:#FFF;font-weight:700;padding:6px;align-content:center;text-align:center}table.multirow,table.normal{border-collapse:collapse;color:#000;min-width:420px;width:100%}table.multirow tr,table.normal td,table.normal tr{padding:4px}table.normal td{height:30px}table.multirow td{height:30px;padding:4px;text-align:center}table.multirow tr:nth-child(even){background-color:#DEE6FF}.highlight td{background-color:#dbff0075}.apheader,.headermenu{background-color:#F8F8F8;padding:8px 12px}.note{color:#444;font-style:italic}.headermenu{border-bottom:1px solid #DDD;height:90px;left:0;position:fixed;right:0;top:0;z-index:1}.bodymenu{margin-top:96px}.menubar{position:inherit;top:55px}.menu{border:solid transparent;border-radius:4px 4px 0 0;border-width:4px 1px 1px;color:#444;padding:4px 16px 8px;white-space:nowrap}.menu.active{background-color:#FFF;border-color:#07D #DDD #FFF;color:#000}.menu:hover{background:#DEF;color:#000}.menu_button{display:none}.on{color:green}.off{color:red}.div_r{background-color:#080;border-radius:4px;margin:2px;padding:1px 10px}.alert,.warning{margin-bottom:15px;padding:20px;color:#fff}.div_br{clear:both}.alert{background-color:#f44336}.warning{background-color:#ffca17}.closebtn{cursor:pointer;font-size:22px;line-height:20px;margin-left:15px;transition:.3s}.closebtn:hover{color:#000}section{overflow-x:auto;width:100%}@media screen and (max-width:960px){.showmenulabel{display:none}.menu{max-width:11vw;max-width:48px}} +.closebtn,h1,h2,h3{font-weight:700}h1,h6{color:#07D}.checkmark:after,.dotmark:after{content:''}.button,.menu{text-decoration:none}.div_l,.menu{float:left}.closebtn,.div_r{float:right;color:#fff}*{box-sizing:border-box;font-family:sans-serif;font-size:12pt;margin:0;padding:0}h1{font-size:16pt;margin:8px 0}h2,h3{font-size:12pt}h2{background-color:#444;color:#FFF;margin:0 -4px;padding:6px}h3{background-color:#EEE;color:#444;margin:16px -4px 0;padding:4px}h6{font-size:10pt}code,kbd,pre,samp,tt,xmp{font-family:monospace,monospace;font-size:1em}.button{background-color:#07D;border:none;border-radius:4px;color:#FFF;margin:4px;padding:4px 16px}.button.help,.checkmark,input,select,textarea{border-color:gray;border-style:solid;border-width:1px}.button.link.wide{display:inline-block;text-align:center;width:100%}.button.link.red{background-color:red}.button.help{border-radius:50%;padding:2px 4px}.checkmark,input,select,textarea{border-radius:4px}.button:hover{background:#369}input:hover,select:hover{background-color:#ccc}input,select,textarea{background-color:#eee;margin:4px;padding:4px 8px}input.wide,select.wide{max-width:500px;width:80%}input.xwide,select.xwide{max-width:500px;width:100%}.widenumber{max-width:500px;width:7em}.container,.container2{font-size:12pt;padding-left:35px;cursor:pointer}.container{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;display:block;margin-left:4px;margin-top:0;position:relative;user-select:none}.container input{cursor:pointer;opacity:0;position:absolute}.checkmark.disabled{background-color:grey}.checkmark{background-color:#eee;height:25px;left:0;position:absolute;top:0;width:25px}.container:hover input~.checkmark{background-color:#ccc}.container input:checked~.checkmark{background-color:#07D}.checkmark:after{display:none;position:absolute}.container input:checked~.checkmark:after,.container2{display:block}.container .checkmark:after{-ms-transform:rotate(45deg);-webkit-transform:rotate(45deg);border:solid #fff;border-width:0 3px 3px 0;height:10px;left:7px;top:3px;transform:rotate(45deg);width:5px}#toastmessage,.dotmark,.logviewer{border-color:gray;border-style:solid}#toastmessage,.dotmark{border-width:1px}.container2{-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;margin-bottom:20px;margin-left:9px;position:relative;user-select:none}.container2 input{cursor:pointer;opacity:0;position:absolute}.dotmark{background-color:#eee;border-radius:50%;height:26px;left:0;position:absolute;top:0;width:26px}.container2:hover input~.dotmark{background-color:#ccc}.container2 input:checked~.dotmark{background-color:#07D}.dotmark:after{display:none;position:absolute}.container2 input:checked~.dotmark:after{display:block}.container2 .dotmark:after{background:#fff;border-radius:50%;height:8px;left:8px;top:8px;width:8px}.logviewer,textarea{font-family:'Lucida Console',Monaco,monospace;max-width:1000px;padding:4px 8px;width:80%}#toastmessage{background-color:#07D;border-radius:4px;bottom:30%;color:#fff;font-size:17px;left:282px;margin-left:-125px;min-width:250px;padding:16px;position:fixed;text-align:center;visibility:hidden;z-index:1}#toastmessage.show{-webkit-animation:fadein .5s,fadeout .5s 2.5s;animation:fadein .5s,fadeout .5s 2.5s;visibility:visible}@-webkit-keyframes fadein{from{bottom:20%;opacity:0}to{bottom:30%;opacity:.9}}@keyframes fadein{from{bottom:20%;opacity:0}to{bottom:30%;opacity:.9}}@-webkit-keyframes fadeout{from{bottom:30%;opacity:.9}to{bottom:0;opacity:0}}@keyframes fadeout{from{bottom:30%;opacity:.9}to{bottom:0;opacity:0}}.level_0{color:#F1F1F1}.level_1{color:#FCFF95}.level_2{color:#9DCEFE}.level_3{color:#A4FC79}.level_4{color:#F2AB39}.level_9{color:#F50}.logviewer{background-color:#272727;color:#F1F1F1;height:530px;overflow:auto}textarea:hover{background-color:#ccc}table.multirow th,table.normal th{background-color:#444;border-color:#888;color:#FFF;font-weight:700;padding:6px;align-content:center;text-align:center}table.multirow,table.normal{border-collapse:collapse;color:#000;min-width:420px;width:100%}table.multirow tr,table.normal td,table.normal tr{padding:4px}table.normal td{height:30px}table.multirow td{height:30px;padding:4px;text-align:center}table.multirow tr:nth-child(even){background-color:#DEE6FF}.highlight td{background-color:#dbff0075}.apheader,.headermenu{background-color:#F8F8F8;padding:8px 12px}.note{color:#444;font-style:italic}.headermenu{border-bottom:1px solid #DDD;height:90px;left:0;position:fixed;right:0;top:0;z-index:1}.bodymenu{margin-top:96px}.menubar{position:inherit;top:55px}.menu{border:solid transparent;border-radius:4px 4px 0 0;border-width:4px 1px 1px;color:#444;padding:4px 16px 8px;white-space:nowrap}.menu.active{background-color:#FFF;border-color:#07D #DDD #FFF;color:#000}.menu:hover{background:#DEF;color:#000}.menu_button{display:none}.on{color:green}.off{color:red}.div_r{background-color:#080;border-radius:4px;margin:2px;padding:1px 10px}.alert,.warning{margin-bottom:15px;padding:20px;color:#fff}.div_br{clear:both}.alert{background-color:#f44336}.warning{background-color:#ffca17}.closebtn{cursor:pointer;font-size:22px;line-height:20px;margin-left:15px;transition:.3s}.closebtn:hover{color:#000}section{overflow-x:auto;width:100%}@media screen and (max-width:960px){.showmenulabel{display:none}.menu{max-width:11vw;max-width:48px}} diff --git a/tools/pio/copy_files.py b/tools/pio/copy_files.py index 23b74aeec..716ae4e85 100644 --- a/tools/pio/copy_files.py +++ b/tools/pio/copy_files.py @@ -18,7 +18,7 @@ def get_max_bin_size(env_name, file_suffix): if "_1M_OTA" in env_name: # max 600 kiB - 16 bytes max_bin_size = 614384 - if "4M316k" in env_name: + if "4M316k" in env_name or "_ESP32_4M2M" in env_name: # ESP32 with 1800k of sketch space. max_bin_size = 1900544 if "factory" in file_suffix: diff --git a/tools/pio/ir_build_check.py b/tools/pio/ir_build_check.py new file mode 100644 index 000000000..40d63f2a8 --- /dev/null +++ b/tools/pio/ir_build_check.py @@ -0,0 +1,17 @@ +Import("env") +import os + +# Check for IR and IRext builds and use a Custom-IR.h file if available, uses Custom-IR-sample.h for defaults +pioenv = env['PIOENV'] +# print("***** PIOENV: ",pioenv) +if "_IR_" in pioenv or "_IRext_" in pioenv: + if os.path.isfile('src/CustomIR.h'): + print("\n\u001b[35m *** CustomIR.h build detected. Create a clean build after changes to CustomIR.h *** \u001b[0m\n") + ir_defines=["-include src/CustomIR.h"] + env.Append(BUILD_FLAGS=ir_defines) + else: + if os.path.isfile('src/CustomIR-sample.h'): + print(" IR build detected. Using CustomIR-sample.h for defaults.") + ir_defines=["-include src/CustomIR-sample.h"] # Use as default settings + env.Append(BUILD_FLAGS=ir_defines) +