├── README.md ├── .gitignore ├── .github ├── workflows │ ├── Arduino-Lint-Check.yml │ └── clang-format-check.yml └── ISSUE_TEMPLATE │ └── bug-report.yml ├── src ├── command_processor.hpp ├── common_header.cpp ├── screenshot_streamer.hpp ├── i2c_master.hpp ├── mlx90640.hpp ├── jpg │ └── jpge.h ├── command_processor.cpp ├── i2c_master.cpp ├── resource │ └── bmp_logo.h ├── mlx90640.cpp └── webserverTask.cpp ├── generate_user_custom.py ├── LICENSE ├── platformio.ini └── .clang-format /README.md: -------------------------------------------------------------------------------- 1 | # T-Lite-Internal-FW (M5StickC-Plus + MLX90640 Thermal HAT) 2 | 3 | ### SKU:K126 4 | 5 | ![T-Lite](https://static-cdn.m5stack.com/resource/docs/products/app/T-Lite/img-1b8ebe07-4556-4641-a487-b6fe6bf8b0c6.jpg) 6 | 7 | ## Related Link 8 | 9 | - [Document & Datasheet](https://docs.m5stack.com/en/app/T-Lite) 10 | 11 | ## License 12 | 13 | - [MIT](LICENSE) 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /.github/workflows/Arduino-Lint-Check.yml: -------------------------------------------------------------------------------- 1 | name: Arduino Lint Check 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | jobs: 8 | lint: 9 | name: Lint Check 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: arduino/arduino-lint-action@v1 14 | with: 15 | library-manager: update 16 | compliance: strict 17 | project-type: all -------------------------------------------------------------------------------- /.github/workflows/clang-format-check.yml: -------------------------------------------------------------------------------- 1 | name: clang-format Check 2 | on: [push, pull_request] 3 | jobs: 4 | formatting-check: 5 | name: Formatting Check 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | path: 10 | - check: './' # path to include 11 | exclude: '' # path to exclude 12 | # - check: 'src' 13 | # exclude: '(Fonts)' # Exclude file paths containing "Fonts" 14 | # - check: 'examples' 15 | # exclude: '' 16 | steps: 17 | - uses: actions/checkout@v3.1.0 18 | - name: Run clang-format style check for C/C++/Protobuf programs. 19 | uses: jidicula/clang-format-action@v4.9.0 20 | with: 21 | clang-format-version: '13' 22 | check-path: ${{ matrix.path['check'] }} 23 | exclude-regex: ${{ matrix.path['exclude'] }} 24 | -------------------------------------------------------------------------------- /src/command_processor.hpp: -------------------------------------------------------------------------------- 1 | //! Copyright (c) M5Stack. All rights reserved. 2 | //! Licensed under the MIT license. 3 | //! See LICENSE file in the project root for full license information. 4 | 5 | #pragma once 6 | 7 | // #define DEBUG 1 8 | #pragma GCC optimize("O3") 9 | 10 | #include 11 | 12 | #include "mlx90640.hpp" 13 | 14 | namespace command_processor { 15 | void setup(void); 16 | bool loop(void); 17 | 18 | bool addData(std::uint8_t value); 19 | void closeData(void); 20 | void prepareTxData(void); 21 | 22 | void setRate(uint8_t rate); 23 | void setFilter(uint8_t level); 24 | void setEmissivity(uint8_t percent); 25 | uint32_t getRecvCount(void); 26 | void updateBattery(void); 27 | int8_t getBatteryLevel(void); 28 | int8_t getBatteryState(void); 29 | m5::MLX90640_Class::temp_data_t* getTemperatureData(void); 30 | } // namespace command_processor 31 | -------------------------------------------------------------------------------- /generate_user_custom.py: -------------------------------------------------------------------------------- 1 | Import("env") 2 | import os 3 | 4 | firmware_name = env.GetProjectOption("custom_firmware_name") 5 | firmware_version = env.GetProjectOption('custom_firmware_version') 6 | firmware_suffix = env.GetProjectOption('custom_firmware_suffix') 7 | firmware_dir = env.GetProjectOption('custom_firmware_dir') 8 | 9 | firmware_bin = '%s_%s%s' % (firmware_name, firmware_version, firmware_suffix) 10 | firmware_path = os.path.join(firmware_dir, firmware_bin) 11 | 12 | app_bin = os.path.join('$BUILD_DIR','${PROGNAME}%s' % firmware_suffix) 13 | 14 | env.AddCustomTarget( 15 | name="firmware", 16 | dependencies=['buildfs'], 17 | actions=[ 18 | '"$PYTHONEXE" "$UPLOADER" --chip esp32 merge_bin ' 19 | '-o %s %s $ESP32_APP_OFFSET %s' 20 | % (firmware_path, ' '.join([ 21 | addr + " " + name for addr, name in env['FLASH_EXTRA_IMAGES'] 22 | ]), app_bin) 23 | ], 24 | title="Generate User Custom") 25 | -------------------------------------------------------------------------------- /src/common_header.cpp: -------------------------------------------------------------------------------- 1 | //! Copyright (c) M5Stack. All rights reserved. 2 | //! Licensed under the MIT license. 3 | //! See LICENSE file in the project root for full license information. 4 | 5 | #include "common_header.h" 6 | 7 | uint8_t config_save_countdown = 0; 8 | uint8_t localize_text_t::localize_index = 0; 9 | 10 | constexpr const char* config_param_t::common_off_on_text[]; 11 | 12 | constexpr const char* config_param_t::misc_language_text[]; 13 | 14 | constexpr const uint8_t config_param_t::misc_cpuspeed_value[]; 15 | constexpr const uint8_t config_param_t::sens_refreshrate_value[]; 16 | constexpr const uint8_t config_param_t::sens_noisefilter_value[]; 17 | constexpr const uint8_t config_param_t::sens_monitorarea_value[]; 18 | constexpr const uint16_t config_param_t::cloud_interval_value[]; 19 | constexpr const uint8_t config_param_t::misc_brightness_value[]; 20 | constexpr const uint8_t config_param_t::misc_volume_value[]; 21 | constexpr const lgfx::IFont* config_param_t::misc_language_value[]; 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 M5Stack 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | 12 | [env] 13 | framework = arduino 14 | platform = espressif32@4.4.0 15 | board = m5stick-c 16 | board_build.partitions = huge_app.csv 17 | board_build.flash_mode = qio 18 | board_build.f_flash = 80000000L 19 | board_build.f_cpu = 240000000L 20 | monitor_speed = 115200 21 | upload_speed = 1500000 22 | monitor_filters = esp32_exception_decoder, time, colorize 23 | lib_deps = bblanchon/ArduinoJson 24 | M5Stack/M5Unified 25 | 26 | [env:release] 27 | build_type = release 28 | build_flags = -DCORE_DEBUG_LEVEL=0 -O3 29 | extra_scripts = post:generate_user_custom.py 30 | custom_firmware_version = 0.0.11 31 | custom_firmware_name = TLite-FW 32 | custom_firmware_suffix = .bin 33 | custom_firmware_dir = r:\ 34 | 35 | [env:debug] 36 | build_type = debug 37 | build_flags = -DCORE_DEBUG_LEVEL=5 38 | -------------------------------------------------------------------------------- /src/screenshot_streamer.hpp: -------------------------------------------------------------------------------- 1 | //! Copyright (c) M5Stack. All rights reserved. 2 | //! Licensed under the MIT license. 3 | //! See LICENSE file in the project root for full license information. 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include "jpg/jpge.h" 11 | 12 | class screenshot_streamer_t : public jpge::output_stream { 13 | QueueHandle_t _queue_canvas; 14 | QueueHandle_t _queue_client; 15 | QueueHandle_t _queue_bufdata; 16 | 17 | WiFiClient* _wifi_client; 18 | 19 | struct queue_ss_t { 20 | M5Canvas* canvas; 21 | uint16_t y; 22 | }; 23 | 24 | struct queue_bufdata_t { 25 | const uint8_t* bufdata; 26 | size_t len; 27 | bool y0; 28 | }; 29 | queue_bufdata_t _q_bufdata; 30 | 31 | uint16_t _y = 0; 32 | uint16_t _width = 0; 33 | uint16_t _height = 0; 34 | bool _is_requested; 35 | 36 | jpge::jpeg_encoder _jpeg_enc; 37 | 38 | public: 39 | screenshot_streamer_t(void); 40 | 41 | static void streamTask(void*); 42 | 43 | bool isRequested(void) const { 44 | return _is_requested; 45 | }; 46 | void requestScreenShot(WiFiClient* client); 47 | 48 | // 新設 49 | bool initCapture(uint16_t width, uint16_t height); 50 | bool addQueue(M5Canvas* canvas, uint16_t y); 51 | 52 | enum process_result_t { 53 | pr_nothing, 54 | pr_error, 55 | pr_progress, 56 | pr_complete, 57 | }; 58 | 59 | process_result_t processCapture(void); 60 | 61 | uint get_size(void) const override { 62 | return 0; 63 | } 64 | bool put_buf(const void* Pbuf, int len) override; 65 | }; 66 | 67 | extern screenshot_streamer_t screenshot_holder; 68 | -------------------------------------------------------------------------------- /src/i2c_master.hpp: -------------------------------------------------------------------------------- 1 | //! Copyright (c) M5Stack. All rights reserved. 2 | //! Licensed under the MIT license. 3 | //! See LICENSE file in the project root for full license information. 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace m5 { 12 | class I2C_Master { 13 | public: 14 | I2C_Master(void){}; 15 | bool init(int i2c_port, int pin_sda, int pin_scl); 16 | bool release(void); 17 | 18 | bool setFreq(uint32_t freq); 19 | bool start(int i2c_addr, bool read, uint32_t freq); 20 | bool restart(int i2c_addr, bool read, uint32_t freq); 21 | bool stop(void); 22 | 23 | bool writeBytes(const uint8_t *data, size_t length); 24 | bool readBytes(uint8_t *readdata, size_t length, bool last_nack = false); 25 | bool writeWords(const uint16_t *data, size_t length); 26 | bool readWords(uint16_t *readdata, size_t length, bool last_nack, int freq); 27 | 28 | bool transactionWrite(int addr, const uint8_t *writedata, uint8_t writelen, 29 | uint32_t freq); 30 | bool transactionRead(int addr, uint8_t *readdata, uint8_t readlen, 31 | uint32_t freq); 32 | bool transactionWriteRead(int addr, const uint8_t *writedata, 33 | uint8_t writelen, uint8_t *readdata, 34 | size_t readlen, uint32_t freq); 35 | 36 | private: 37 | static void _isr_handler(void *arg); 38 | bool _readword_inner(i2c_dev_t *dev, uint8_t *data, size_t length, 39 | bool last_nack); 40 | enum isr_mode_t { 41 | isr_nojob, 42 | isr_readword, 43 | }; 44 | isr_mode_t _isr_mode = isr_nojob; 45 | uint8_t *_isr_recv_buf = nullptr; 46 | size_t _isr_recv_done_len = 0; 47 | size_t _isr_recv_remain_len = 0; 48 | bool _isr_result = 0; 49 | xSemaphoreHandle _isr_semaphore = nullptr; 50 | 51 | gpio_num_t _pin_sda; 52 | gpio_num_t _pin_scl; 53 | uint32_t _freq; 54 | uint8_t _i2c_port; 55 | bool _wait_ack; 56 | 57 | enum state_t { state_disconnect, state_write, state_read, state_error }; 58 | state_t _state; 59 | 60 | bool i2c_wait(bool flg_stop = false); 61 | void i2c_stop(void); 62 | 63 | void save_reg(void); 64 | void load_reg(void); 65 | 66 | uint32_t scl_high_period; 67 | uint32_t scl_low_period; 68 | uint32_t scl_start_hold; 69 | uint32_t scl_rstart_setup; 70 | uint32_t scl_stop_hold; 71 | uint32_t scl_stop_setup; 72 | uint32_t sda_hold; 73 | uint32_t sda_sample; 74 | uint32_t fifo_conf; 75 | uint32_t timeout; 76 | uint32_t filter_cfg; 77 | uint32_t scl_filter; 78 | uint32_t sda_filter; 79 | }; 80 | }; // namespace m5 81 | -------------------------------------------------------------------------------- /src/mlx90640.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace m5 { 7 | class I2C_Master; 8 | 9 | class MLX90640_Class { 10 | public: 11 | // Amount of bit shift required for conversion to Celsius temperature. 12 | static constexpr int DATA_RATIO_SHIFT = 7; 13 | 14 | // Magnification required for conversion to Celsius temperature. 15 | static constexpr int DATA_RATIO_VALUE = 1 << DATA_RATIO_SHIFT; 16 | 17 | // Negative amount required for conversion to Celsius temperature. 18 | static constexpr int DATA_OFFSET = 64; 19 | 20 | MLX90640_Class(void) : _i2c{nullptr} {}; 21 | 22 | static constexpr size_t PIXEL_ROWS = 24; 23 | static constexpr size_t PIXEL_COLS = 32; 24 | static constexpr size_t FRAME_DATA_BYTES = 834 * 2; 25 | static constexpr size_t TEMP_DATA_BYTES = 32 * 24 * 2; 26 | static constexpr size_t DATA_ARRAY_LEN = 16 * 24; 27 | 28 | enum refresh_rate_t { 29 | rate_0_5Hz, 30 | rate_1Hz, 31 | rate_2Hz, 32 | rate_4Hz, 33 | rate_8Hz, 34 | rate_16Hz, 35 | rate_32Hz, 36 | rate_64Hz, 37 | }; 38 | 39 | #pragma pack(push) 40 | #pragma pack(1) 41 | struct temperature_info_t { 42 | uint16_t temp; 43 | uint8_t x; 44 | uint8_t y; 45 | }; 46 | struct temp_data_t { 47 | uint8_t refresh_control; 48 | uint8_t subpage; 49 | uint16_t med_temp; 50 | uint16_t avg_temp; 51 | temperature_info_t diff_info; 52 | temperature_info_t min_info; 53 | temperature_info_t max_info; 54 | uint16_t data[16 * 24]; 55 | }; 56 | #pragma pack(pop) 57 | 58 | bool init(I2C_Master* i2c); 59 | void setRate(refresh_rate_t rate); 60 | inline refresh_rate_t getRate(void) const { 61 | return _refresh_rate; 62 | } 63 | void update(void); 64 | 65 | bool writeReg(uint16_t reg, uint16_t value); 66 | bool writeReg(uint16_t reg, const uint16_t* data, size_t len); 67 | bool readReg(uint16_t reg, uint16_t* data, size_t len); 68 | 69 | /// read raw frame data from MLX90640 70 | /// framedata require size 834 * 2 Bytes 71 | bool readFrameData(uint16_t* framedata); 72 | 73 | /// calc by raw frame data 74 | /// tempdata require 32*24*2 Byte, and framedata require size 834 * 2 Bytes 75 | void calcTempData(const uint16_t* framedata, temp_data_t* tempdata, 76 | const temp_data_t* prev_tempdata, uint32_t filter_level, 77 | uint8_t monitor_width, uint8_t monitor_height); 78 | 79 | void calcTempData(const uint16_t* framedata, temp_data_t* tempdata, 80 | float emissivity); 81 | 82 | private: 83 | I2C_Master* _i2c; 84 | refresh_rate_t _refresh_rate = (refresh_rate_t)-1; 85 | uint32_t _i2c_freq = 800000; 86 | uint8_t _i2c_addr = 0x33; 87 | }; 88 | } // namespace m5 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | # Source: 2 | # https://github.com/Tinyu-Zhao/M5-Depends/blob/main/.github/ISSUE_TEMPLATE/bug-report.yml 3 | # See: 4 | # https://docs.github.com/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms 5 | 6 | name: Bug report 7 | description: Report a problem with the code in this repository. 8 | labels: 9 | - "type: bug" 10 | body: 11 | - type: markdown 12 | attributes: 13 | value: "Thank you for opening an issue on an M5Stack Arduino library repository.\n\ 14 | To improve the speed of resolution please review the following guidelines and common troubleshooting steps below before creating the issue:\n\ 15 | If you have any UIFLOW questions you can ask in the [special board](https://community.m5stack.com/category/5/uiflow), there will be many enthusiastic friends to help you.\n\ 16 | Do not use GitHub issues for troubleshooting projects and issues. Instead use the forums at https://community.m5stack.com to ask questions and troubleshoot why something isn't working as expected. In many cases the problem is a common issue that you will more quickly receive help from the forum community. GitHub issues are meant for known defects in the code. If you don't know if there is a defect in the code then start with troubleshooting on the forum first." 17 | - type: textarea 18 | id: description 19 | attributes: 20 | label: Describe the bug 21 | description: A clear and concise description of what the bug is. 22 | validations: 23 | required: true 24 | - type: textarea 25 | id: reproduce 26 | attributes: 27 | label: To reproduce 28 | description: Provide the specific set of steps we can follow to reproduce the problem. 29 | placeholder: | 30 | 1. In this environment... 31 | 2. With this config... 32 | 3. Run '...' 33 | 4. See error... 34 | validations: 35 | required: true 36 | - type: textarea 37 | id: expected 38 | attributes: 39 | label: Expected behavior 40 | description: What would you expect to happen after following those instructions? 41 | validations: 42 | required: true 43 | - type: textarea 44 | id: screenshots 45 | attributes: 46 | label: Screenshots 47 | description: If applicable, add screenshots to help explain your problem. 48 | validations: 49 | required: false 50 | - type: textarea 51 | id: information 52 | attributes: 53 | label: Environment 54 | description: | 55 | If applicable, add screenshots to help explain your problem. 56 | examples: 57 | - **OS**: Ubuntu 20.04 58 | - **IDE & IDE Version**: Arduino 1.8.19 Or Platform IO v2.5.0 59 | - **Repository Version**: 0.4.0 60 | value: | 61 | - OS: 62 | - IDE &IDE Version: 63 | - Repository Version: 64 | validations: 65 | required: false 66 | - type: textarea 67 | id: additional 68 | attributes: 69 | label: Additional context 70 | description: Add any additional information here. 71 | validations: 72 | required: false 73 | - type: checkboxes 74 | id: checklist 75 | attributes: 76 | label: Issue checklist 77 | description: Please double-check that you have done each of the following things before submitting the issue. 78 | options: 79 | - label: I searched for previous reports in [the issue tracker](https://github.com/m5stack/M5Stack/issues?q=) 80 | required: true 81 | - label: My report contains all necessary details 82 | required: true 83 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveMacros: true 7 | AlignConsecutiveAssignments: true 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlines: Left 10 | AlignOperands: true 11 | AlignTrailingComments: true 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: Never 16 | AllowShortCaseLabelsOnASingleLine: false 17 | AllowShortFunctionsOnASingleLine: false 18 | AllowShortLambdasOnASingleLine: All 19 | AllowShortIfStatementsOnASingleLine: WithoutElse 20 | AllowShortLoopsOnASingleLine: true 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: true 24 | AlwaysBreakTemplateDeclarations: Yes 25 | BinPackArguments: true 26 | BinPackParameters: true 27 | BraceWrapping: 28 | AfterCaseLabel: false 29 | AfterClass: false 30 | AfterControlStatement: false 31 | AfterEnum: false 32 | AfterFunction: false 33 | AfterNamespace: false 34 | AfterObjCDeclaration: false 35 | AfterStruct: false 36 | AfterUnion: false 37 | AfterExternBlock: false 38 | BeforeCatch: false 39 | BeforeElse: false 40 | IndentBraces: false 41 | SplitEmptyFunction: true 42 | SplitEmptyRecord: true 43 | SplitEmptyNamespace: true 44 | BreakBeforeBinaryOperators: None 45 | BreakBeforeBraces: Attach 46 | BreakBeforeInheritanceComma: false 47 | BreakInheritanceList: BeforeColon 48 | BreakBeforeTernaryOperators: true 49 | BreakConstructorInitializersBeforeComma: false 50 | BreakConstructorInitializers: BeforeColon 51 | BreakAfterJavaFieldAnnotations: false 52 | BreakStringLiterals: true 53 | ColumnLimit: 80 54 | CommentPragmas: '^ IWYU pragma:' 55 | CompactNamespaces: false 56 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 57 | ConstructorInitializerIndentWidth: 4 58 | ContinuationIndentWidth: 4 59 | Cpp11BracedListStyle: true 60 | DeriveLineEnding: true 61 | DerivePointerAlignment: true 62 | DisableFormat: false 63 | ExperimentalAutoDetectBinPacking: false 64 | FixNamespaceComments: true 65 | ForEachMacros: 66 | - foreach 67 | - Q_FOREACH 68 | - BOOST_FOREACH 69 | IncludeBlocks: Regroup 70 | IncludeCategories: 71 | - Regex: '^' 72 | Priority: 2 73 | SortPriority: 0 74 | - Regex: '^<.*\.h>' 75 | Priority: 1 76 | SortPriority: 0 77 | - Regex: '^<.*' 78 | Priority: 2 79 | SortPriority: 0 80 | - Regex: '.*' 81 | Priority: 3 82 | SortPriority: 0 83 | IncludeIsMainRegex: '([-_](test|unittest))?$' 84 | IncludeIsMainSourceRegex: '' 85 | IndentCaseLabels: true 86 | IndentGotoLabels: true 87 | IndentPPDirectives: None 88 | IndentWidth: 4 89 | IndentWrappedFunctionNames: false 90 | JavaScriptQuotes: Leave 91 | JavaScriptWrapImports: true 92 | KeepEmptyLinesAtTheStartOfBlocks: false 93 | MacroBlockBegin: '' 94 | MacroBlockEnd: '' 95 | MaxEmptyLinesToKeep: 1 96 | NamespaceIndentation: None 97 | ObjCBinPackProtocolList: Never 98 | ObjCBlockIndentWidth: 2 99 | ObjCSpaceAfterProperty: false 100 | ObjCSpaceBeforeProtocolList: true 101 | PenaltyBreakAssignment: 2 102 | PenaltyBreakBeforeFirstCallParameter: 1 103 | PenaltyBreakComment: 300 104 | PenaltyBreakFirstLessLess: 120 105 | PenaltyBreakString: 1000 106 | PenaltyBreakTemplateDeclaration: 10 107 | PenaltyExcessCharacter: 1000000 108 | PenaltyReturnTypeOnItsOwnLine: 200 109 | PointerAlignment: Left 110 | RawStringFormats: 111 | - Language: Cpp 112 | Delimiters: 113 | - cc 114 | - CC 115 | - cpp 116 | - Cpp 117 | - CPP 118 | - 'c++' 119 | - 'C++' 120 | CanonicalDelimiter: '' 121 | BasedOnStyle: google 122 | - Language: TextProto 123 | Delimiters: 124 | - pb 125 | - PB 126 | - proto 127 | - PROTO 128 | EnclosingFunctions: 129 | - EqualsProto 130 | - EquivToProto 131 | - PARSE_PARTIAL_TEXT_PROTO 132 | - PARSE_TEST_PROTO 133 | - PARSE_TEXT_PROTO 134 | - ParseTextOrDie 135 | - ParseTextProtoOrDie 136 | CanonicalDelimiter: '' 137 | BasedOnStyle: google 138 | ReflowComments: true 139 | SortIncludes: false 140 | SortUsingDeclarations: true 141 | SpaceAfterCStyleCast: false 142 | SpaceAfterLogicalNot: false 143 | SpaceAfterTemplateKeyword: true 144 | SpaceBeforeAssignmentOperators: true 145 | SpaceBeforeCpp11BracedList: false 146 | SpaceBeforeCtorInitializerColon: true 147 | SpaceBeforeInheritanceColon: true 148 | SpaceBeforeParens: ControlStatements 149 | SpaceBeforeRangeBasedForLoopColon: true 150 | SpaceInEmptyBlock: false 151 | SpaceInEmptyParentheses: false 152 | SpacesBeforeTrailingComments: 2 153 | SpacesInAngles: false 154 | SpacesInConditionalStatement: false 155 | SpacesInContainerLiterals: true 156 | SpacesInCStyleCastParentheses: false 157 | SpacesInParentheses: false 158 | SpacesInSquareBrackets: false 159 | SpaceBeforeSquareBrackets: false 160 | Standard: Auto 161 | StatementMacros: 162 | - Q_UNUSED 163 | - QT_REQUIRE_VERSION 164 | TabWidth: 4 165 | UseCRLF: false 166 | UseTab: Never 167 | ... 168 | -------------------------------------------------------------------------------- /src/jpg/jpge.h: -------------------------------------------------------------------------------- 1 | // jpge.h - C++ class for JPEG compression. 2 | // Public domain, Rich Geldreich 3 | // Alex Evans: Added RGBA support, linear memory allocator. 4 | #ifndef JPEG_ENCODER_H 5 | #define JPEG_ENCODER_H 6 | 7 | #include 8 | 9 | namespace jpge { 10 | typedef char int8; 11 | typedef unsigned char uint8; 12 | typedef signed short int16; 13 | typedef signed int int32; 14 | typedef unsigned short uint16; 15 | typedef unsigned int uint32; 16 | typedef unsigned int uint; 17 | 18 | // JPEG chroma subsampling factors. Y_ONLY (grayscale images) and H2V2 (color 19 | // images) are the most common. 20 | enum subsampling_t { Y_ONLY = 0, H1V1 = 1, H2V1 = 2, H2V2 = 3 }; 21 | 22 | // JPEG compression parameters structure. 23 | struct params { 24 | inline params() : m_quality(85), m_subsampling(H2V2) { 25 | } 26 | 27 | inline bool check() const { 28 | if ((m_quality < 1) || (m_quality > 100)) { 29 | return false; 30 | } 31 | if ((uint)m_subsampling > (uint)H2V2) { 32 | return false; 33 | } 34 | return true; 35 | } 36 | 37 | // Quality: 1-100, higher is better. Typical values are around 50-95. 38 | int m_quality; 39 | 40 | // m_subsampling: 41 | // 0 = Y (grayscale) only 42 | // 1 = H1V1 subsampling (YCbCr 1x1x1, 3 blocks per MCU) 43 | // 2 = H2V1 subsampling (YCbCr 2x1x1, 4 blocks per MCU) 44 | // 3 = H2V2 subsampling (YCbCr 4x1x1, 6 blocks per MCU-- very common) 45 | subsampling_t m_subsampling; 46 | }; 47 | 48 | // Output stream abstract class - used by the jpeg_encoder class to write to the 49 | // output stream. put_buf() is generally called with len==JPGE_OUT_BUF_SIZE 50 | // bytes, but for headers it'll be called with smaller amounts. 51 | class output_stream { 52 | public: 53 | virtual ~output_stream(){}; 54 | virtual bool put_buf(const void *Pbuf, int len) = 0; 55 | virtual uint get_size() const = 0; 56 | }; 57 | 58 | // Lower level jpeg_encoder class - useful if more control is needed than the 59 | // above helper functions. 60 | class jpeg_encoder { 61 | public: 62 | jpeg_encoder(); 63 | ~jpeg_encoder(); 64 | 65 | // Initializes the compressor. 66 | // pStream: The stream object to use for writing compressed data. 67 | // params - Compression parameters structure, defined above. 68 | // width, height - Image dimensions. 69 | // channels - May be 1, or 3. 1 indicates grayscale, 3 indicates RGB source 70 | // data. Returns false on out of memory or if a stream write fails. 71 | bool init(output_stream *pStream, int width, int height, int src_channels, 72 | const params &comp_params = params()); 73 | bool reinit(int quality); 74 | 75 | // Call this method with each source scanline. 76 | // width * src_channels bytes per scanline is expected (RGB or Y format). 77 | // You must call with NULL after all scanlines are processed to finish 78 | // compression. Returns false on out of memory or if a stream write fails. 79 | bool process_scanline(const void *pScanline); 80 | bool process_scanline565(const void *pScanline); 81 | void process_mcu_row(); 82 | 83 | // Deinitializes the compressor, freeing any allocated memory. May be called 84 | // at any time. 85 | void deinit(); 86 | 87 | private: 88 | jpeg_encoder(const jpeg_encoder &); 89 | jpeg_encoder &operator=(const jpeg_encoder &); 90 | 91 | typedef int32 sample_array_t; 92 | enum { JPGE_OUT_BUF_SIZE = 1500, JPGE_OUT_BUF_COUNT = 4 }; 93 | 94 | output_stream *m_pStream; 95 | params m_params; 96 | uint8 m_num_components; 97 | uint8 m_comp_h_samp[3], m_comp_v_samp[3]; 98 | int m_image_x, m_image_y, m_image_bpp, m_image_bpl; 99 | int m_image_x_mcu, m_image_y_mcu; 100 | int m_image_bpl_xlt, m_image_bpl_mcu; 101 | int m_mcus_per_row; 102 | int m_mcu_x, m_mcu_y; 103 | int16 *m_mcu_lines[16]; 104 | uint8 m_mcu_y_ofs; 105 | sample_array_t m_sample_array[64]; 106 | int16 m_coefficient_array[64]; 107 | 108 | int32 m_last_dc_val[3]; 109 | uint8 m_out_buf_array[JPGE_OUT_BUF_COUNT][JPGE_OUT_BUF_SIZE]; 110 | uint8 m_out_buf_index; 111 | uint8 *m_pOut_buf; 112 | uint m_out_buf_left; 113 | uint32 m_bit_buffer; 114 | uint m_bits_in; 115 | uint8 m_pass_num; 116 | bool m_all_stream_writes_succeeded; 117 | 118 | bool jpg_open(int p_x_res, int p_y_res, int src_channels); 119 | 120 | void flush_output_buffer(); 121 | void put_bits(uint bits, uint len); 122 | 123 | void emit_byte(uint i); 124 | void emit_word(uint i); 125 | void emit_marker(uint8 marker); 126 | 127 | void emit_jfif_app0(); 128 | void emit_dqt(); 129 | void emit_sof(); 130 | void emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag); 131 | void emit_dhts(); 132 | void emit_sos(); 133 | 134 | void compute_quant_table(uint8 *pDst, int32 *pDst32, const uint8 *pSrc); 135 | void load_quantized_coefficients(int component_num); 136 | 137 | void load_block_8_8_grey(int x); 138 | void load_block_8_8(int x, int y, int c); 139 | void load_block_16_8(int x, int c); 140 | void load_block_16_8_8(int x, int c); 141 | 142 | void code_coefficients_pass_two(int component_num); 143 | void code_block(int component_num); 144 | 145 | // void process_mcu_row(); 146 | bool process_end_of_image(); 147 | void load_mcu(const void *src); 148 | void load_mcu565(const void *src); 149 | void clear(); 150 | void init(); 151 | }; 152 | 153 | } // namespace jpge 154 | 155 | #endif // JPEG_ENCODER 156 | -------------------------------------------------------------------------------- /src/command_processor.cpp: -------------------------------------------------------------------------------- 1 | //! Copyright (c) M5Stack. All rights reserved. 2 | //! Licensed under the MIT license. 3 | //! See LICENSE file in the project root for full license information. 4 | 5 | #include "command_processor.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include "i2c_master.hpp" 24 | #include "mlx90640.hpp" 25 | 26 | namespace command_processor { 27 | 28 | static constexpr const uint8_t noise_tbl[] = { 29 | 0, 0, 0, 1, 2, 5, 8, 13, 20, 28, 39, 52, 67, 86, 107, 132, 160, 30 | 0, 0, 0, 1, 3, 5, 9, 14, 20, 29, 39, 52, 68, 86, 108, 132, 160, 31 | 0, 0, 1, 2, 3, 6, 9, 14, 21, 30, 41, 54, 69, 88, 109, 134, 162, 32 | 1, 1, 1, 2, 4, 7, 11, 16, 23, 32, 42, 56, 72, 90, 112, 137, 165, 33 | 1, 2, 2, 3, 5, 8, 12, 18, 25, 34, 45, 59, 75, 94, 116, 141, 170, 34 | 3, 3, 4, 5, 7, 10, 15, 21, 28, 37, 49, 63, 79, 98, 121, 146, 175, 35 | 4, 5, 6, 7, 10, 13, 18, 24, 32, 42, 54, 68, 85, 104, 127, 153, 182, 36 | 7, 7, 8, 10, 13, 17, 22, 28, 37, 47, 59, 74, 91, 111, 134, 161, 191, 37 | 11, 11, 12, 14, 17, 21, 27, 34, 42, 53, 66, 81, 99, 119, 143, 170, 200, 38 | 15, 15, 17, 19, 22, 27, 33, 40, 49, 60, 74, 89, 108, 129, 153, 181, 212, 39 | 21, 21, 22, 25, 29, 33, 40, 48, 57, 69, 83, 99, 118, 140, 165, 193, 225, 40 | 27, 28, 29, 32, 36, 41, 48, 56, 67, 79, 93, 110, 130, 152, 178, 207, 239, 41 | 35, 36, 38, 41, 45, 51, 58, 67, 77, 90, 105, 123, 143, 166, 193, 222, 255}; 42 | 43 | static m5::I2C_Master _i2c_in; 44 | static m5::MLX90640_Class _mlx; 45 | 46 | static constexpr size_t MLX_TEMP_ARRAY_SIZE = 4; 47 | static constexpr size_t MLX_FRAMEDATA_ARRAY_SIZE = 4; 48 | static volatile int _idx_framedata = -1; 49 | static volatile int _idx_tempdata = MLX_TEMP_ARRAY_SIZE - 1; 50 | static uint16_t* _mlx_framedatas[MLX_FRAMEDATA_ARRAY_SIZE]; 51 | static m5::MLX90640_Class::temp_data_t* _mlx_tempdatas[MLX_TEMP_ARRAY_SIZE] = { 52 | nullptr}; 53 | static m5::MLX90640_Class::temp_data_t* _temp_data = nullptr; 54 | // static int16_t* _diff_data; 55 | 56 | static m5::MLX90640_Class::refresh_rate_t _refresh_rate; 57 | static uint8_t _noise_filter = 8; 58 | static uint8_t _emissivity = 98; 59 | 60 | /* clang-format off */ 61 | static inline volatile uint32_t* get_gpio_hi_reg(int_fast8_t pin) { return (pin & 32) ? &GPIO.out1_w1ts.val : &GPIO.out_w1ts; } 62 | static inline volatile uint32_t* get_gpio_lo_reg(int_fast8_t pin) { return (pin & 32) ? &GPIO.out1_w1tc.val : &GPIO.out_w1tc; } 63 | static inline bool gpio_in(int_fast8_t pin) { return ((pin & 32) ? GPIO.in1.data : GPIO.in) & (1 << (pin & 31)); } 64 | static inline void gpio_hi(int_fast8_t pin) { *get_gpio_hi_reg(pin) = 1 << (pin & 31); } 65 | static inline void gpio_lo(int_fast8_t pin) { *get_gpio_lo_reg(pin) = 1 << (pin & 31); } 66 | /* clang-format on */ 67 | 68 | static int8_t _battery_state = 0; 69 | static int8_t _battery_level = 0; 70 | static bool _battery_request = false; 71 | 72 | void updateBattery(void) { 73 | if (M5.Power.getType() == m5::Power_Class::pmic_ip5306) { 74 | _battery_request = true; 75 | } else { 76 | _battery_state = M5.Power.isCharging(); 77 | _battery_level = M5.Power.getBatteryLevel(); 78 | } 79 | } 80 | 81 | int8_t getBatteryLevel(void) { 82 | return _battery_level; 83 | } 84 | 85 | int8_t getBatteryState(void) { 86 | return _battery_state; 87 | } 88 | 89 | static void IRAM_ATTR mlxTask(void* main_handle) { 90 | gpio_num_t PIN_IN_SDA = GPIO_NUM_0; 91 | gpio_num_t PIN_IN_SCL = GPIO_NUM_26; 92 | i2c_port_t PORT_I2C = I2C_NUM_0; 93 | switch (M5.getBoard()) { 94 | case m5::board_t::board_M5StackCore2: 95 | PIN_IN_SDA = (gpio_num_t)M5.Ex_I2C.getSDA(); 96 | PIN_IN_SCL = (gpio_num_t)M5.Ex_I2C.getSCL(); 97 | break; 98 | case m5::board_t::board_M5Stack: 99 | PIN_IN_SDA = (gpio_num_t)M5.In_I2C.getSDA(); 100 | PIN_IN_SCL = (gpio_num_t)M5.In_I2C.getSCL(); 101 | PORT_I2C = I2C_NUM_1; 102 | break; 103 | default: 104 | break; 105 | } 106 | 107 | // running... 108 | size_t discard_count = 2; 109 | uint8_t error_count = 255; 110 | for (;;) { 111 | if (error_count >= 128) { 112 | if (error_count == 128) { // 強制的にSTOPコンディションを送信する 113 | ESP_EARLY_LOGD("mlxTask", "I2C force stop"); 114 | gpio_config_t io_conf; 115 | io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; 116 | io_conf.pull_up_en = GPIO_PULLUP_DISABLE; 117 | io_conf.intr_type = GPIO_INTR_DISABLE; 118 | io_conf.mode = GPIO_MODE_OUTPUT; 119 | io_conf.pin_bit_mask = (uint64_t)1 << PIN_IN_SDA; 120 | gpio_config(&io_conf); 121 | io_conf.pin_bit_mask = (uint64_t)1 << PIN_IN_SCL; 122 | gpio_config(&io_conf); 123 | for (int i = 0; i < 20; ++i) { 124 | vTaskDelay(1); 125 | gpio_lo(PIN_IN_SCL); 126 | vTaskDelay(1); 127 | gpio_lo(PIN_IN_SDA); 128 | vTaskDelay(1); 129 | gpio_hi(PIN_IN_SCL); 130 | vTaskDelay(1); 131 | gpio_hi(PIN_IN_SDA); 132 | } 133 | } 134 | _i2c_in.release(); 135 | // initialize sensor. 136 | _i2c_in.init(PORT_I2C, PIN_IN_SDA, PIN_IN_SCL); 137 | int retry = 16; 138 | 139 | while (!_mlx.init(&_i2c_in)) { 140 | ESP_EARLY_LOGD("mlxTask", "I2C int"); 141 | vTaskDelay(100); 142 | } 143 | 144 | _mlx.setRate(_refresh_rate); 145 | 146 | error_count = 0; 147 | discard_count = 2; 148 | } 149 | m5::MLX90640_Class::refresh_rate_t rate = _mlx.getRate(); 150 | if (rate != _refresh_rate) { 151 | rate = _refresh_rate; 152 | _mlx.setRate(rate); 153 | // Discard twice because invalid data is obtained immediately after 154 | // refresh rate change. 155 | discard_count = 2; 156 | } 157 | int idx = (_idx_framedata + 1); 158 | if (idx >= MLX_FRAMEDATA_ARRAY_SIZE) { 159 | idx = 0; 160 | } 161 | auto recv = _mlx.readFrameData(_mlx_framedatas[idx]); 162 | ++error_count; 163 | if (recv) { 164 | error_count = 0; 165 | // ++recv_count; 166 | if (discard_count) { 167 | --discard_count; 168 | } else { 169 | _idx_framedata = idx; 170 | xTaskNotifyGive(main_handle); 171 | } 172 | } else { 173 | static constexpr const uint8_t delay_tbl[] = {32, 16, 8, 4, 174 | 2, 1, 1, 1}; 175 | vTaskDelay(delay_tbl[rate]); 176 | } 177 | if (_battery_request) { 178 | _battery_request = false; 179 | _battery_state = M5.Power.isCharging(); 180 | _battery_level = M5.Power.getBatteryLevel(); 181 | } 182 | } 183 | vTaskDelete(nullptr); 184 | } 185 | 186 | m5::MLX90640_Class::temp_data_t* getTemperatureData(void) { 187 | return _mlx_tempdatas[_idx_tempdata]; 188 | } 189 | 190 | void setRate(uint8_t rate) { 191 | _refresh_rate = (m5::MLX90640_Class::refresh_rate_t)rate; 192 | } 193 | void setFilter(uint8_t level) { 194 | _noise_filter = level; 195 | } 196 | void setEmissivity(uint8_t percent) { 197 | _emissivity = percent > 100 ? 100 : percent; 198 | } 199 | 200 | void setup(void) { 201 | for (int i = 0; i < MLX_FRAMEDATA_ARRAY_SIZE; ++i) { 202 | _mlx_framedatas[i] = (uint16_t*)heap_caps_malloc( 203 | m5::MLX90640_Class::FRAME_DATA_BYTES, MALLOC_CAP_DMA); 204 | memset(_mlx_framedatas[i], 0x2C, m5::MLX90640_Class::FRAME_DATA_BYTES); 205 | } 206 | 207 | xTaskCreatePinnedToCore(mlxTask, "mlxTask", 8192, 208 | xTaskGetCurrentTaskHandle(), 20, nullptr, 209 | APP_CPU_NUM); 210 | _refresh_rate = m5::MLX90640_Class::rate_32Hz; 211 | _noise_filter = 8; 212 | _emissivity = 98; // <- default : 98.0 % 213 | 214 | for (int i = 0; i < MLX_TEMP_ARRAY_SIZE; ++i) { 215 | _mlx_tempdatas[i] = (m5::MLX90640_Class::temp_data_t*)heap_caps_malloc( 216 | sizeof(m5::MLX90640_Class::temp_data_t), MALLOC_CAP_DMA); 217 | memset(_mlx_tempdatas[i], 0, sizeof(m5::MLX90640_Class::temp_data_t)); 218 | } 219 | } 220 | 221 | bool IRAM_ATTR loop(void) { 222 | static int prev_idx_framedata = -1; 223 | if (prev_idx_framedata == _idx_framedata) return false; 224 | // if (prev_idx_framedata != _idx_framedata) 225 | 226 | { 227 | #if DEBUG == 1 228 | { // debug 229 | if (_idx_framedata != 230 | ((prev_idx_framedata + 1) & (MLX_FRAMEDATA_ARRAY_SIZE - 1))) { 231 | ESP_LOGE(LOGNAME, "prev_idx_frame:%d idx_frame:%d", 232 | prev_idx_framedata, _idx_framedata); 233 | } 234 | } 235 | #endif 236 | // prev_idx_framedata = _idx_framedata; 237 | prev_idx_framedata = prev_idx_framedata < MLX_FRAMEDATA_ARRAY_SIZE - 1 238 | ? prev_idx_framedata + 1 239 | : 0; 240 | // auto prev_temp_data = _mlx_tempdatas[_idx_tempdata]; 241 | int idx = 242 | _idx_tempdata < MLX_TEMP_ARRAY_SIZE - 1 ? _idx_tempdata + 1 : 0; 243 | _temp_data = _mlx_tempdatas[idx]; 244 | 245 | float emissivity = ((float)_emissivity) / 100.0f; 246 | _mlx.calcTempData(_mlx_framedatas[prev_idx_framedata], _temp_data, 247 | emissivity); 248 | 249 | auto prev_temp_data = _mlx_tempdatas[(idx + MLX_TEMP_ARRAY_SIZE - 2) % 250 | MLX_TEMP_ARRAY_SIZE]; 251 | 252 | static constexpr int16_t noise_filter_level[] = {181, 256, 362, 512, 253 | 724, 1024, 1448, 2048}; 254 | int filter_value = noise_filter_level[_mlx.getRate()]; 255 | int filter_level = (filter_value * (_noise_filter & 0xF)) >> 6; 256 | 257 | /// ノイズフィルタ処理 258 | if (filter_level) { 259 | bool subPage = _temp_data->subpage; 260 | for (size_t i = 0; i < 384; ++i) { 261 | int ilPattern = (i >> 4) & 1; 262 | int pixelNumber = (i << 1) + ((ilPattern ^ subPage) & 1); 263 | /// (前回の温度と比較して一定以上の差がないと反応させない) 264 | int x = (pixelNumber & 31) - 15; 265 | if (x < 0) { 266 | x = ~x; 267 | } 268 | int y = (pixelNumber >> 5) - 13; 269 | if (y < 0) { 270 | y = ~y; 271 | } 272 | // 外周ピクセルほどノイズが多いため、ピクセル位置に応じてテーブルから補正係数を掛ける; 273 | int noise_filter = 274 | (filter_level * (96 + noise_tbl[x + (y * 17)])) >> 8; 275 | 276 | int32_t temp = _temp_data->data[i]; 277 | int diff = temp - prev_temp_data->data[i]; 278 | if (abs(diff) > noise_filter) { 279 | temp += (diff < 0) ? noise_filter : -noise_filter; 280 | } else { 281 | temp = prev_temp_data->data[i]; 282 | } 283 | _temp_data->data[i] = temp; 284 | } 285 | } 286 | _idx_tempdata = idx; 287 | } 288 | return true; 289 | } 290 | 291 | } // namespace command_processor -------------------------------------------------------------------------------- /src/i2c_master.cpp: -------------------------------------------------------------------------------- 1 | //! Copyright (c) M5Stack. All rights reserved. 2 | //! Licensed under the MIT license. 3 | //! See LICENSE file in the project root for full license information. 4 | 5 | #include "i2c_master.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | namespace m5 { 18 | #if !defined(I2C_ACK_ERR_INT_RAW_M) 19 | #define I2C_ACK_ERR_INT_RAW_M I2C_NACK_INT_RAW_M 20 | #endif 21 | __attribute__((unused)) static inline unsigned long millis(void) { 22 | return (unsigned long)(esp_timer_get_time() / 1000ULL); 23 | } 24 | __attribute__((unused)) static inline unsigned long micros(void) { 25 | return (unsigned long)(esp_timer_get_time()); 26 | } 27 | 28 | #if __has_include() 29 | #include 30 | static inline periph_module_t getPeriphModule(i2c_port_t num) { 31 | return i2c_periph_signal[num].module; 32 | } 33 | static inline std::uint8_t getPeriphIntSource(i2c_port_t num) { 34 | return i2c_periph_signal[num].irq; 35 | } 36 | #else 37 | static inline periph_module_t getPeriphModule(i2c_port_t num) { 38 | return num == 0 ? PERIPH_I2C0_MODULE : PERIPH_I2C1_MODULE; 39 | } 40 | static inline std::uint8_t getPeriphIntSource(i2c_port_t num) { 41 | return num == 0 ? ETS_I2C_EXT0_INTR_SOURCE : ETS_I2C_EXT1_INTR_SOURCE; 42 | } 43 | #endif 44 | 45 | #if SOC_I2C_NUM == 1 46 | static inline i2c_dev_t *IRAM_ATTR getDev(i2c_port_t num) { 47 | return &I2C0; 48 | } 49 | #else 50 | static __attribute__((always_inline)) inline i2c_dev_t *IRAM_ATTR 51 | getDev(i2c_port_t num) { 52 | return num == 0 ? &I2C0 : &I2C1; 53 | } 54 | #endif 55 | 56 | #if defined(CONFIG_IDF_TARGET_ESP32C3) 57 | 58 | static i2c_dev_t *get_i2c_dev(int num) { 59 | return &I2C0; 60 | } 61 | static void updateDev(i2c_dev_t *dev) { 62 | dev->ctr.conf_upgate = 1; 63 | } 64 | static volatile uint32_t *getFifoAddr(int num) { 65 | return &I2C0.fifo_data.val; 66 | } 67 | 68 | static constexpr int i2c_cmd_start = 6; 69 | static constexpr int i2c_cmd_write = 1; 70 | static constexpr int i2c_cmd_read = 3; 71 | static constexpr int i2c_cmd_stop = 2; 72 | static constexpr int i2c_cmd_end = 4; 73 | 74 | #else 75 | 76 | static __attribute__((always_inline)) inline i2c_dev_t *get_i2c_dev(int num) { 77 | return num == 0 ? &I2C0 : &I2C1; 78 | } 79 | 80 | static __attribute__((always_inline)) inline void updateDev(i2c_dev_t *dev) { 81 | } 82 | 83 | static __attribute__((always_inline)) inline volatile uint32_t *getFifoAddr( 84 | int num) { 85 | return (volatile uint32_t *)((num == 0) ? 0x6001301c : 0x6002701c); 86 | } 87 | static __attribute__((always_inline)) inline std::uint32_t IRAM_ATTR 88 | getRxFifoCount(i2c_dev_t *dev) { 89 | return dev->status_reg.rx_fifo_cnt; 90 | } 91 | 92 | static constexpr int i2c_cmd_start = 0; 93 | static constexpr int i2c_cmd_write = 1; 94 | static constexpr int i2c_cmd_read = 2; 95 | static constexpr int i2c_cmd_stop = 3; 96 | static constexpr int i2c_cmd_end = 4; 97 | 98 | #endif 99 | 100 | static void IRAM_ATTR i2c_set_cmd(i2c_dev_t *dev, uint8_t index, 101 | uint8_t op_code, uint8_t byte_num, 102 | bool ack_value = false) { 103 | uint32_t cmd_val = byte_num | 104 | ((op_code == i2c_cmd_write || op_code == i2c_cmd_stop) 105 | ? 0x100 106 | : 0) // writeおよびstop時はACK_ENを有効にする; 107 | | ack_value << 10 // 受信時ACK応答値 108 | | op_code << 11; 109 | dev->command[index].val = cmd_val; 110 | } 111 | 112 | static constexpr int I2C_7BIT_ADDR_MIN = 0x08; 113 | static constexpr int I2C_7BIT_ADDR_MAX = 0x77; 114 | static constexpr int I2C_10BIT_ADDR_MAX = 1023; 115 | 116 | void IRAM_ATTR I2C_Master::save_reg(void) { 117 | auto i2c_dev = get_i2c_dev(_i2c_port); 118 | scl_high_period = i2c_dev->scl_high_period.val; 119 | scl_low_period = i2c_dev->scl_low_period.val; 120 | scl_start_hold = i2c_dev->scl_start_hold.val; 121 | scl_rstart_setup = i2c_dev->scl_rstart_setup.val; 122 | scl_stop_hold = i2c_dev->scl_stop_hold.val; 123 | scl_stop_setup = i2c_dev->scl_stop_setup.val; 124 | sda_hold = i2c_dev->sda_hold.val; 125 | sda_sample = i2c_dev->sda_sample.val; 126 | fifo_conf = i2c_dev->fifo_conf.val; 127 | timeout = i2c_dev->timeout.val; 128 | #if defined(I2C_FILTER_CFG_REG) 129 | filter_cfg = dev->filter_cfg.val; 130 | #else 131 | scl_filter = i2c_dev->scl_filter_cfg.val; 132 | sda_filter = i2c_dev->sda_filter_cfg.val; 133 | #endif 134 | } 135 | 136 | void IRAM_ATTR I2C_Master::load_reg(void) { 137 | auto i2c_dev = get_i2c_dev(_i2c_port); 138 | i2c_dev->scl_high_period.val = scl_high_period; 139 | i2c_dev->scl_low_period.val = scl_low_period; 140 | i2c_dev->scl_start_hold.val = scl_start_hold; 141 | i2c_dev->scl_rstart_setup.val = scl_rstart_setup; 142 | i2c_dev->scl_stop_hold.val = scl_stop_hold; 143 | i2c_dev->scl_stop_setup.val = scl_stop_setup; 144 | i2c_dev->sda_hold.val = sda_hold; 145 | i2c_dev->sda_sample.val = sda_sample; 146 | i2c_dev->fifo_conf.val = fifo_conf; 147 | i2c_dev->timeout.val = timeout; 148 | #if defined(I2C_FILTER_CFG_REG) 149 | dev->filter_cfg.val = filter_cfg; 150 | #else 151 | i2c_dev->scl_filter_cfg.val = scl_filter; 152 | i2c_dev->sda_filter_cfg.val = sda_filter; 153 | #endif 154 | } 155 | 156 | void IRAM_ATTR I2C_Master::i2c_stop(void) { 157 | static constexpr int I2C_CLR_BUS_HALF_PERIOD_US = 158 | 5; // use standard 100kHz data rate 159 | static constexpr int I2C_CLR_BUS_SCL_NUM = 9; 160 | 161 | gpio_set_level(_pin_sda, 1); 162 | gpio_set_direction(_pin_sda, GPIO_MODE_INPUT_OUTPUT_OD); 163 | 164 | gpio_set_level(_pin_scl, 1); 165 | gpio_set_direction(_pin_scl, GPIO_MODE_OUTPUT_OD); 166 | 167 | auto mod = getPeriphModule(_i2c_port); 168 | // ESP-IDF環境でperiph_module_disableを使うと、後でenableできなくなる問題が起きたためコメントアウト; 169 | // periph_module_disable(mod); 170 | gpio_set_level(_pin_scl, 0); 171 | 172 | // SDAがHIGHになるまでクロック送出しながら待機する。; 173 | int i = 0; 174 | while (!gpio_get_level(_pin_sda) && (i++ < I2C_CLR_BUS_SCL_NUM)) { 175 | ets_delay_us(I2C_CLR_BUS_HALF_PERIOD_US); 176 | gpio_set_level(_pin_scl, 1); 177 | ets_delay_us(I2C_CLR_BUS_HALF_PERIOD_US); 178 | gpio_set_level(_pin_scl, 0); 179 | } 180 | gpio_set_level(_pin_sda, 0); // setup for STOP 181 | periph_module_enable(mod); 182 | gpio_set_level(_pin_scl, 1); 183 | periph_module_reset(mod); 184 | // gpio_set_level(pin_sda, 1); // STOP, SDA low -> high while SCL is HIGH 185 | i2c_set_pin((i2c_port_t)_i2c_port, _pin_sda, _pin_scl, 186 | gpio_pullup_t::GPIO_PULLUP_ENABLE, 187 | gpio_pullup_t::GPIO_PULLUP_ENABLE, I2C_MODE_MASTER); 188 | } 189 | 190 | bool IRAM_ATTR I2C_Master::i2c_wait(bool flg_stop) { 191 | auto i2c_dev = get_i2c_dev(_i2c_port); 192 | if (i2c_dev == nullptr) { 193 | return false; 194 | } 195 | if (_state == state_t::state_error) { 196 | return false; 197 | } 198 | bool res = true; 199 | if (_state == state_t::state_disconnect) { 200 | return res; 201 | } 202 | 203 | typeof(i2c_dev->int_raw) int_raw; 204 | static constexpr uint32_t intmask = I2C_ACK_ERR_INT_RAW_M | 205 | I2C_END_DETECT_INT_RAW_M | 206 | I2C_ARBITRATION_LOST_INT_RAW_M; 207 | if (_wait_ack) { 208 | int_raw.val = i2c_dev->int_raw.val; 209 | if (!(int_raw.val & intmask)) { 210 | uint32_t us = micros(); 211 | #if defined(CONFIG_IDF_TARGET_ESP32C3) 212 | uint32_t us_limit = (dev->scl_high_period.period + 213 | dev->scl_low_period.period + 16) * 214 | (1 + dev->sr.tx_fifo_cnt); 215 | #else 216 | uint32_t us_limit = (i2c_dev->scl_high_period.period + 217 | i2c_dev->scl_low_period.period + 20) * 218 | (2 + i2c_dev->status_reg.tx_fifo_cnt); 219 | #endif 220 | // static uint32_t prev = -1; 221 | // if (prev != us_limit) 222 | // { 223 | // prev = us_limit; 224 | // ESP_LOGW("us_limit","us_limit:%d", us_limit); 225 | // } 226 | do { 227 | int_raw.val = i2c_dev->int_raw.val; 228 | } while (!(int_raw.val & intmask) && ((micros() - us) <= us_limit)); 229 | } 230 | i2c_dev->int_clr.val = int_raw.val; 231 | #if !defined(CONFIG_IDF_TARGET) || defined(CONFIG_IDF_TARGET_ESP32) 232 | if (!int_raw.end_detect || int_raw.ack_err) 233 | #else 234 | if (!int_raw.end_detect || int_raw.nack) 235 | #endif 236 | { 237 | res = false; 238 | _state = state_t::state_error; 239 | } 240 | } 241 | 242 | if (flg_stop || !res) { 243 | if (_state == state_t::state_read || 244 | !int_raw.end_detect) { // force stop 245 | i2c_stop(); 246 | } else { 247 | i2c_set_cmd(i2c_dev, 0, i2c_cmd_stop, 0); 248 | i2c_set_cmd(i2c_dev, 1, i2c_cmd_end, 0); 249 | i2c_dev->ctr.trans_start = 1; 250 | static constexpr uint32_t intmask = 251 | I2C_ACK_ERR_INT_RAW_M | I2C_TIME_OUT_INT_RAW_M | 252 | I2C_END_DETECT_INT_RAW_M | I2C_ARBITRATION_LOST_INT_RAW_M | 253 | I2C_TRANS_COMPLETE_INT_RAW_M; 254 | uint32_t ms = millis(); 255 | taskYIELD(); 256 | while (!(i2c_dev->int_raw.val & intmask) && ((millis() - ms) < 14)) 257 | ; 258 | #if !defined(CONFIG_IDF_TARGET) || defined(CONFIG_IDF_TARGET_ESP32) 259 | if (res && i2c_dev->int_raw.ack_err) 260 | #else 261 | if (res && dev->int_raw.nack) 262 | #endif 263 | { 264 | res = false; 265 | } 266 | } 267 | load_reg(); 268 | if (res) { 269 | _state = state_t::state_disconnect; 270 | } 271 | } 272 | _wait_ack = false; 273 | return res; 274 | } 275 | 276 | bool IRAM_ATTR I2C_Master::_readword_inner(i2c_dev_t *dev, uint8_t *data, 277 | size_t length, bool last_nack) { 278 | size_t len = ((length - 1) & 127) + 1; 279 | length -= len; 280 | auto res = i2c_wait(false); 281 | if (!res) { 282 | ESP_LOGW("I2C_Master", "readWords error : ack wait"); 283 | return false; 284 | } 285 | 286 | _isr_recv_buf = data; 287 | _isr_recv_remain_len = length; 288 | _isr_mode = isr_mode_t::isr_readword; 289 | 290 | i2c_set_cmd(dev, 0, i2c_cmd_read, (len << 1) - 1); 291 | i2c_set_cmd(dev, 1, i2c_cmd_read, 1, length == 0 && last_nack); 292 | i2c_set_cmd(dev, 2, i2c_cmd_end, 0); 293 | updateDev(dev); 294 | 295 | static constexpr uint32_t intmask = 296 | I2C_ACK_ERR_INT_RAW_M | I2C_TIME_OUT_INT_RAW_M | 297 | I2C_RXFIFO_FULL_INT_RAW_M | I2C_END_DETECT_INT_RAW_M | 298 | I2C_ARBITRATION_LOST_INT_RAW_M; 299 | dev->int_clr.val = ~0u; 300 | dev->int_ena.val = intmask; 301 | dev->ctr.trans_start = 1; 302 | return true; 303 | } 304 | 305 | void IRAM_ATTR I2C_Master::_isr_handler(void *arg) { 306 | auto me = (I2C_Master *)arg; 307 | if (me->_isr_mode == isr_mode_t::isr_nojob) { 308 | return; 309 | } 310 | 311 | bool result = true; 312 | auto dev = getDev(me->_i2c_port); 313 | do { 314 | typeof(dev->int_status) int_sts; 315 | int_sts.val = dev->int_status.val; 316 | dev->int_clr.val = int_sts.val; 317 | 318 | if (me->_isr_mode == isr_mode_t::isr_readword) { 319 | auto fifo_addr = getFifoAddr(me->_i2c_port); 320 | auto dst = me->_isr_recv_buf; 321 | auto recv_done_len = me->_isr_recv_done_len; 322 | 323 | while (1 < getRxFifoCount(dev)) { 324 | auto tmp = *fifo_addr; 325 | *dst++ = *fifo_addr; 326 | *dst++ = tmp; 327 | ++recv_done_len; 328 | } 329 | me->_isr_recv_buf = dst; 330 | me->_isr_recv_done_len = recv_done_len; 331 | if (int_sts.ack_err || int_sts.time_out || 332 | int_sts.arbitration_lost) { 333 | me->_isr_result = false; 334 | } 335 | if (int_sts.end_detect) { 336 | if (me->_isr_recv_remain_len) { 337 | me->_isr_result = me->_readword_inner( 338 | dev, dst, me->_isr_recv_remain_len, true); 339 | } else { 340 | if (me->_isr_semaphore) { 341 | BaseType_t xHigherPriorityTaskWoken = pdTRUE; 342 | xSemaphoreGiveFromISR(me->_isr_semaphore, 343 | &xHigherPriorityTaskWoken); 344 | portYIELD_FROM_ISR(); 345 | } 346 | } 347 | } 348 | } 349 | } while (dev->int_status.val); 350 | } 351 | 352 | bool I2C_Master::init(int i2c_port, int pin_sda, int pin_scl) { 353 | if (i2c_port >= I2C_NUM_MAX) { 354 | return false; 355 | } 356 | this->_i2c_port = i2c_port; 357 | this->save_reg(); 358 | release(); 359 | this->_pin_scl = (gpio_num_t)pin_scl; 360 | this->_pin_sda = (gpio_num_t)pin_sda; 361 | i2c_stop(); 362 | this->load_reg(); 363 | 364 | esp_intr_alloc(getPeriphIntSource(i2c_port), 365 | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL3, _isr_handler, 366 | this, nullptr); 367 | 368 | return true; 369 | } 370 | 371 | bool I2C_Master::release(void) { 372 | gpio_reset_pin((gpio_num_t)_pin_sda); 373 | gpio_reset_pin((gpio_num_t)_pin_scl); 374 | return true; 375 | } 376 | 377 | bool I2C_Master::setFreq(uint32_t freq) { 378 | auto i2c_dev = get_i2c_dev(_i2c_port); 379 | if (i2c_dev == nullptr) return false; 380 | 381 | _freq = freq; 382 | static constexpr uint32_t MIN_I2C_CYCLE = 40; // 35; 383 | #if defined(CONFIG_IDF_TARGET_ESP32C3) 384 | uint32_t src_clock = 40 * 1000 * 1000; // XTAL clock 385 | #else 386 | rtc_cpu_freq_config_t cpu_freq_conf; 387 | rtc_clk_cpu_freq_get_config(&cpu_freq_conf); 388 | uint32_t src_clock = 80 * 1000 * 1000; 389 | if (cpu_freq_conf.freq_mhz < 80) { 390 | src_clock = 391 | (cpu_freq_conf.source_freq_mhz * 1000000) / cpu_freq_conf.div; 392 | } 393 | #endif 394 | 395 | auto cycle = std::min( 396 | 32767u, std::max(MIN_I2C_CYCLE, (src_clock / (freq + 1) + 1))); 397 | freq = src_clock / cycle; 398 | 399 | #if defined(CONFIG_IDF_TARGET_ESP32S2) 400 | dev->ctr.ref_always_on = 1; 401 | #endif 402 | 403 | #if defined(I2C_FILTER_CFG_REG) 404 | dev->filter_cfg.scl_en = cycle > 64; 405 | dev->filter_cfg.scl_thres = 0; 406 | dev->filter_cfg.sda_en = cycle > 64; 407 | dev->filter_cfg.sda_thres = 0; 408 | 409 | uint32_t scl_high_offset = 410 | (dev->filter_cfg.scl_en ? (dev->filter_cfg.scl_thres <= 2 411 | ? 8 412 | : (6 + dev->filter_cfg.scl_thres)) 413 | : 7); 414 | dev->clk_conf.sclk_sel = 0; 415 | #else 416 | i2c_dev->scl_filter_cfg.en = cycle > 64; 417 | i2c_dev->scl_filter_cfg.thres = 0; 418 | i2c_dev->sda_filter_cfg.en = cycle > 64; 419 | i2c_dev->sda_filter_cfg.thres = 0; 420 | /// ESP32 TRM page 286 Table 57: SCL Frequency Configuration 421 | uint32_t scl_high_offset = 422 | (i2c_dev->scl_filter_cfg.en 423 | ? (i2c_dev->scl_filter_cfg.thres <= 2 424 | ? 8 425 | : (6 + i2c_dev->scl_filter_cfg.thres)) 426 | : 7); 427 | 428 | #endif 429 | 430 | uint32_t period_total = cycle - scl_high_offset - 1; 431 | uint32_t scl_high_period = std::max(18, (period_total - 10) >> 1); 432 | uint32_t scl_low_period = period_total - scl_high_period; 433 | 434 | i2c_dev->scl_high_period.period = scl_high_period; 435 | i2c_dev->scl_low_period.period = scl_low_period; 436 | #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) 437 | dev->scl_high_period.scl_wait_high_period = scl_high_period >> 2; 438 | #endif 439 | i2c_dev->sda_hold.time = 440 | std::min(1023, (i2c_dev->scl_high_period.period >> 1)); 441 | i2c_dev->sda_sample.time = 442 | std::min(1023, (i2c_dev->scl_low_period.period >> 1)); 443 | 444 | if (freq > 400000) { 445 | cycle = cycle * freq / 400000; 446 | } else if (cycle > ((1 << 10) - 1)) { 447 | cycle = (1 << 10) - 1; 448 | } 449 | // the clock num after the STOP bit's posedge 450 | i2c_dev->scl_stop_hold.time = cycle << 1; 451 | // the clock num between the posedge of SCL and the posedge of SDA 452 | i2c_dev->scl_stop_setup.time = cycle; 453 | // the clock num between the negedge of SDA and 454 | // the negedge of SCL for start mark 455 | i2c_dev->scl_start_hold.time = cycle; 456 | // the clock num between the posedge of SCL and 457 | // the negedge of SDA for restart mark 458 | i2c_dev->scl_rstart_setup.time = cycle; 459 | 460 | return true; 461 | } 462 | 463 | bool I2C_Master::restart(int i2c_addr, bool read, uint32_t freq) { 464 | auto i2c_dev = get_i2c_dev(_i2c_port); 465 | if (i2c_dev == nullptr) { 466 | return false; 467 | } 468 | if (i2c_addr < I2C_7BIT_ADDR_MIN || i2c_addr > I2C_10BIT_ADDR_MAX) 469 | return false; 470 | 471 | auto res = i2c_wait(false); 472 | if (!res) return res; 473 | 474 | auto fifo_addr = getFifoAddr(_i2c_port); 475 | i2c_set_cmd(i2c_dev, 0, i2c_cmd_start, 0); 476 | i2c_set_cmd(i2c_dev, 2, i2c_cmd_end, 0); 477 | if (i2c_addr <= I2C_7BIT_ADDR_MAX) { // 7bitアドレスの場合; 478 | *fifo_addr = 479 | i2c_addr << 1 | (read ? I2C_MASTER_READ : I2C_MASTER_WRITE); 480 | i2c_set_cmd(i2c_dev, 1, i2c_cmd_write, 1); 481 | } else { // 10bitアドレスの場合; 482 | *fifo_addr = 0xF0 | (i2c_addr >> 8) << 1 | I2C_MASTER_WRITE; 483 | *fifo_addr = i2c_addr; 484 | i2c_set_cmd(i2c_dev, 1, i2c_cmd_write, 2); 485 | if (read) { // 10bitアドレスのread要求の場合; 486 | *fifo_addr = 0xF0 | (i2c_addr >> 8) << 1 | I2C_MASTER_READ; 487 | i2c_set_cmd(i2c_dev, 2, i2c_cmd_start, 0); 488 | i2c_set_cmd(i2c_dev, 3, i2c_cmd_read, 1); 489 | i2c_set_cmd(i2c_dev, 4, i2c_cmd_end, 0); 490 | } 491 | } 492 | 493 | if (_state == state_t::state_disconnect || _freq != freq) { 494 | setFreq(freq); 495 | } 496 | 497 | updateDev(i2c_dev); 498 | i2c_dev->int_clr.val = 0x1FFFF; 499 | i2c_dev->ctr.trans_start = 1; 500 | _state = read ? state_t::state_read : state_t::state_write; 501 | _wait_ack = true; 502 | return res; 503 | } 504 | 505 | bool I2C_Master::start(int i2c_addr, bool read, uint32_t freq) { 506 | auto i2c_dev = get_i2c_dev(_i2c_port); 507 | if (i2c_dev == nullptr) return false; 508 | 509 | save_reg(); 510 | 511 | #if defined(CONFIG_IDF_TARGET_ESP32C3) 512 | if (dev->sr.bus_busy) 513 | #else 514 | if (i2c_dev->status_reg.bus_busy) 515 | #endif 516 | { 517 | auto ms = micros(); 518 | do { 519 | taskYIELD(); 520 | } 521 | #if defined(CONFIG_IDF_TARGET_ESP32C3) 522 | while (dev->sr.bus_busy && micros() - ms < 128); 523 | #else 524 | while (i2c_dev->status_reg.bus_busy && micros() - ms < 128); 525 | #endif 526 | } 527 | 528 | #if SOC_I2C_SUPPORT_HW_FSM_RST 529 | dev->ctr.fsm_rst = 1; 530 | #endif 531 | 532 | #if defined(CONFIG_IDF_TARGET_ESP32C3) 533 | dev->timeout.time_out_value = 31; 534 | dev->timeout.time_out_en = 1; 535 | #else 536 | i2c_dev->timeout.tout = 0xFFFFF; // max 13ms 537 | #endif 538 | i2c_dev->int_ena.val = 0; 539 | // ---------- i2c_ll_master_init 540 | typeof(i2c_dev->ctr) ctrl_reg; 541 | ctrl_reg.val = 0; 542 | ctrl_reg.ms_mode = 1; // master mode 543 | ctrl_reg.clk_en = 1; 544 | ctrl_reg.sda_force_out = 1; 545 | ctrl_reg.scl_force_out = 1; 546 | i2c_dev->ctr.val = ctrl_reg.val; 547 | // ---------- i2c_ll_master_init 548 | typeof(i2c_dev->fifo_conf) fifo_conf_reg; 549 | fifo_conf_reg.val = 0; 550 | fifo_conf_reg.tx_fifo_rst = 1; 551 | fifo_conf_reg.rx_fifo_rst = 1; 552 | i2c_dev->fifo_conf.val = fifo_conf_reg.val; 553 | 554 | fifo_conf_reg.val = 0; 555 | fifo_conf_reg.rx_fifo_full_thrhd = 24; 556 | fifo_conf_reg.tx_fifo_empty_thrhd = 4; 557 | #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) 558 | fifo_conf_reg.fifo_prt_en = 1; 559 | #endif 560 | i2c_dev->fifo_conf.val = fifo_conf_reg.val; 561 | 562 | _state = state_t::state_disconnect; 563 | 564 | return restart(i2c_addr, read, freq); 565 | } 566 | 567 | bool I2C_Master::stop(void) { 568 | return i2c_wait(true); 569 | } 570 | 571 | bool I2C_Master::writeBytes(const uint8_t *data, size_t length) { 572 | auto i2c_dev = get_i2c_dev(_i2c_port); 573 | if (i2c_dev == nullptr) { 574 | return false; 575 | } 576 | if (_state == state_t::state_error) { 577 | return false; 578 | } 579 | if (_state == state_t::state_read) { 580 | return false; 581 | } 582 | if (!length) { 583 | return true; 584 | } 585 | 586 | static constexpr int txfifo_limit = 32; 587 | auto dev = get_i2c_dev(_i2c_port); 588 | auto fifo_addr = getFifoAddr(_i2c_port); 589 | size_t len = ((length - 1) & (txfifo_limit - 1)) + 1; 590 | do { 591 | if (!i2c_wait(false)) { 592 | ESP_LOGW("I2C_Master", "writeBytes error : ack wait"); 593 | return false; 594 | } 595 | size_t idx = 0; 596 | do { 597 | *fifo_addr = data[idx]; 598 | } while (++idx != len); 599 | i2c_set_cmd(dev, 0, i2c_cmd_write, len); 600 | i2c_set_cmd(dev, 1, i2c_cmd_end, 0); 601 | updateDev(dev); 602 | dev->ctr.trans_start = 1; 603 | _wait_ack = true; 604 | data += len; 605 | length -= len; 606 | len = txfifo_limit; 607 | } while (length); 608 | return true; 609 | } 610 | 611 | bool I2C_Master::writeWords(const uint16_t *data, size_t length) { 612 | auto i2c_dev = get_i2c_dev(_i2c_port); 613 | if (i2c_dev == nullptr) { 614 | return false; 615 | } 616 | if (_state == state_t::state_error) { 617 | return false; 618 | } 619 | if (_state == state_t::state_read) { 620 | return false; 621 | } 622 | if (!length) { 623 | return true; 624 | } 625 | 626 | static constexpr int txfifo_limit = 16; 627 | auto dev = get_i2c_dev(_i2c_port); 628 | auto fifo_addr = getFifoAddr(_i2c_port); 629 | size_t len = ((length - 1) & (txfifo_limit - 1)) + 1; 630 | do { 631 | if (!i2c_wait(false)) { 632 | ESP_LOGW("I2C_Master", "writeWords error : ack wait"); 633 | return false; 634 | } 635 | size_t idx = 0; 636 | do { 637 | *fifo_addr = data[idx] >> 8; 638 | *fifo_addr = data[idx] & 0xFF; 639 | } while (++idx != len); 640 | i2c_set_cmd(dev, 0, i2c_cmd_write, len << 1); 641 | i2c_set_cmd(dev, 1, i2c_cmd_end, 0); 642 | updateDev(dev); 643 | dev->ctr.trans_start = 1; 644 | _wait_ack = true; 645 | data += len; 646 | length -= len; 647 | len = txfifo_limit; 648 | } while (length); 649 | return true; 650 | } 651 | /* 652 | bool I2C_Master::readBytes(uint8_t *readdata, size_t length, bool last_nack) { 653 | auto i2c_dev = get_i2c_dev(_i2c_port); 654 | if (i2c_dev == nullptr) { 655 | return false; 656 | } 657 | if (_state == state_t::state_error) { 658 | return false; 659 | } 660 | if (_state == state_t::state_write) { 661 | return false; 662 | } 663 | if (!length) { 664 | return true; 665 | } 666 | bool res = true; 667 | 668 | static constexpr uint32_t intmask = 669 | I2C_ACK_ERR_INT_RAW_M | I2C_TIME_OUT_INT_RAW_M | 670 | I2C_END_DETECT_INT_RAW_M | I2C_ARBITRATION_LOST_INT_RAW_M; 671 | auto fifo_addr = getFifoAddr(_i2c_port); 672 | auto dev = get_i2c_dev(_i2c_port); 673 | size_t len = 0; 674 | 675 | uint32_t us_limit = 676 | (dev->scl_high_period.period + dev->scl_low_period.period + 16); 677 | do { 678 | len = ((length - 1) & 31) + 1; 679 | length -= len; 680 | res = i2c_wait(false); 681 | if (!res) { 682 | ESP_LOGW("I2C_Master", "readBytes error : ack wait"); 683 | break; 684 | } 685 | if (length == 0 && last_nack) { 686 | if (len == 1) { 687 | i2c_set_cmd(dev, 0, i2c_cmd_read, len, true); 688 | i2c_set_cmd(dev, 1, i2c_cmd_end, 0); 689 | } else { 690 | i2c_set_cmd(dev, 0, i2c_cmd_read, len - 1); 691 | i2c_set_cmd(dev, 1, i2c_cmd_read, 1, true); 692 | i2c_set_cmd(dev, 2, i2c_cmd_end, 0); 693 | } 694 | } else { 695 | i2c_set_cmd(dev, 0, i2c_cmd_read, len); 696 | i2c_set_cmd(dev, 1, i2c_cmd_end, 0); 697 | } 698 | dev->int_clr.val = intmask; 699 | dev->ctr.trans_start = 1; 700 | do { 701 | uint32_t us = micros(); 702 | #if defined(CONFIG_IDF_TARGET_ESP32C3) 703 | while (0 == dev->sr.rx_fifo_cnt && !(dev->int_raw.val & intmask) && 704 | ((micros() - us) <= us_limit)) 705 | ; 706 | if (0 != dev->sr.rx_fifo_cnt) 707 | #else 708 | while (0 == dev->status_reg.rx_fifo_cnt && 709 | !(dev->int_raw.val & intmask) && 710 | ((micros() - us) <= us_limit)) 711 | ; 712 | if (0 != dev->status_reg.rx_fifo_cnt) 713 | #endif 714 | { 715 | *readdata++ = *fifo_addr; 716 | } else { 717 | i2c_stop(); 718 | ESP_LOGW("I2C_Master", "readBytes error : read timeout"); 719 | res = false; 720 | _state = state_t::state_error; 721 | return res; 722 | } 723 | } while (--len); 724 | } while (length); 725 | 726 | return res; 727 | } 728 | //*/ 729 | bool I2C_Master::readWords(uint16_t *readdata, size_t length, bool last_nack, 730 | int freq) { 731 | if (!length) { 732 | return true; 733 | } 734 | if (_state == state_t::state_error || _state == state_t::state_write) { 735 | return false; 736 | } 737 | auto i2c_dev = get_i2c_dev(_i2c_port); 738 | if (i2c_dev == nullptr) { 739 | return false; 740 | } 741 | if (freq) { 742 | i2c_wait(false); 743 | setFreq(freq); 744 | } 745 | 746 | _isr_semaphore = xSemaphoreCreateBinary(); 747 | _isr_recv_done_len = 0; 748 | _readword_inner(get_i2c_dev(_i2c_port), (uint8_t *)readdata, length, 749 | last_nack); 750 | // 想定の8倍の時間(+10 msec)で応答が得られなければ通信失敗と見なして帰る 751 | auto result = xSemaphoreTake(_isr_semaphore, 752 | ((18 * 1000 * length) / (freq >> 3)) + 10); 753 | vSemaphoreDelete(_isr_semaphore); 754 | _isr_semaphore = nullptr; 755 | return _isr_result && (result == pdTRUE); 756 | } 757 | /* 758 | bool I2C_Master::readWords(uint16_t *readdata, size_t length, bool last_nack) { 759 | auto i2c_dev = get_i2c_dev(_i2c_port); 760 | if (i2c_dev == nullptr) { 761 | return false; 762 | } 763 | if (_state == state_t::state_error) { 764 | return false; 765 | } 766 | if (_state == state_t::state_write) { 767 | return false; 768 | } 769 | if (!length) { 770 | return true; 771 | } 772 | bool res = true; 773 | 774 | static constexpr uint32_t intmask = 775 | I2C_ACK_ERR_INT_RAW_M | I2C_TIME_OUT_INT_RAW_M | 776 | I2C_END_DETECT_INT_RAW_M | I2C_ARBITRATION_LOST_INT_RAW_M; 777 | auto fifo_addr = getFifoAddr(_i2c_port); 778 | auto dev = get_i2c_dev(_i2c_port); 779 | size_t len = 0; 780 | 781 | uint32_t us_limit = 782 | (dev->scl_high_period.period + dev->scl_low_period.period + 16); 783 | do { 784 | len = ((length - 1) & 15) + 1; 785 | length -= len; 786 | res = i2c_wait(false); 787 | if (!res) { 788 | ESP_LOGW("I2C_Master", "readWords error : ack wait"); 789 | break; 790 | } 791 | i2c_set_cmd(dev, 0, i2c_cmd_read, (len << 1) - 1); 792 | i2c_set_cmd(dev, 1, i2c_cmd_read, 1, length == 0 && last_nack); 793 | i2c_set_cmd(dev, 2, i2c_cmd_end, 0); 794 | 795 | dev->int_clr.val = intmask; 796 | dev->ctr.trans_start = 1; 797 | if (length) { 798 | taskYIELD(); 799 | } 800 | do { 801 | uint32_t us = micros(); 802 | #if defined(CONFIG_IDF_TARGET_ESP32C3) 803 | while (0 == dev->sr.rx_fifo_cnt && !(dev->int_raw.val & intmask) && 804 | ((micros() - us) <= us_limit)) 805 | ; 806 | if (0 != dev->sr.rx_fifo_cnt) 807 | #else 808 | while (2 > dev->status_reg.rx_fifo_cnt && 809 | !(dev->int_raw.val & intmask) && 810 | ((micros() - us) <= us_limit)) 811 | ; 812 | if (1 < dev->status_reg.rx_fifo_cnt) 813 | #endif 814 | { 815 | auto tmp = *fifo_addr << 8; 816 | *readdata++ = tmp + *fifo_addr; 817 | } else { 818 | i2c_stop(); 819 | ESP_LOGW("I2C_Master", "readWords error : read timeout"); 820 | res = false; 821 | _state = state_t::state_error; 822 | } 823 | } while (--len); 824 | } while (length); 825 | 826 | return res; 827 | } 828 | //*/ 829 | bool I2C_Master::transactionWrite(int addr, const uint8_t *writedata, 830 | uint8_t writelen, uint32_t freq) { 831 | return start(addr, false, freq) && writeBytes(writedata, writelen) && 832 | stop(); 833 | } 834 | 835 | bool I2C_Master::transactionRead(int addr, uint8_t *readdata, uint8_t readlen, 836 | uint32_t freq) { 837 | return start(addr, true, freq) && readBytes(readdata, readlen) && stop(); 838 | } 839 | 840 | bool I2C_Master::transactionWriteRead(int addr, const uint8_t *writedata, 841 | uint8_t writelen, uint8_t *readdata, 842 | size_t readlen, uint32_t freq) { 843 | return start(addr, false, freq) && writeBytes(writedata, writelen) && 844 | restart(addr, true, freq) && readBytes(readdata, readlen) && stop(); 845 | } 846 | }; // namespace m5 847 | -------------------------------------------------------------------------------- /src/resource/bmp_logo.h: -------------------------------------------------------------------------------- 1 | 2 | static constexpr const unsigned char bmp_logo[6958] = { 3 | 0x42, 0x4d, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x01, 4 | 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x87, 0x00, 5 | 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x19, 6 | 0x00, 0x00, 0x11, 0x17, 0x00, 0x00, 0x11, 0x17, 0x00, 0x00, 0x6a, 0x00, 7 | 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 8 | 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x05, 9 | 0x07, 0x00, 0x00, 0x06, 0x08, 0x00, 0x06, 0x09, 0x03, 0x00, 0x00, 0x08, 10 | 0x0a, 0x00, 0x10, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x0f, 0x00, 0x00, 0x10, 11 | 0x09, 0x00, 0x00, 0x0f, 0x13, 0x00, 0x26, 0x09, 0x1c, 0x00, 0x0c, 0x13, 12 | 0x06, 0x00, 0x10, 0x10, 0x10, 0x00, 0x20, 0x19, 0x00, 0x00, 0x00, 0x15, 13 | 0x1b, 0x00, 0x00, 0x17, 0x1d, 0x00, 0x13, 0x1c, 0x09, 0x00, 0x09, 0x13, 14 | 0x30, 0x00, 0x00, 0x1a, 0x21, 0x00, 0x00, 0x20, 0x13, 0x00, 0x00, 0x1c, 15 | 0x23, 0x00, 0x30, 0x26, 0x00, 0x00, 0x19, 0x26, 0x0c, 0x00, 0x20, 0x20, 16 | 0x20, 0x00, 0x00, 0x16, 0x59, 0x00, 0x00, 0x24, 0x2e, 0x00, 0x59, 0x16, 17 | 0x43, 0x00, 0x00, 0x26, 0x30, 0x00, 0x20, 0x30, 0x10, 0x00, 0x40, 0x33, 18 | 0x00, 0x00, 0x00, 0x30, 0x1c, 0x00, 0x2d, 0x2d, 0x2d, 0x00, 0x00, 0x2e, 19 | 0x3a, 0x00, 0x00, 0x2f, 0x3b, 0x00, 0x30, 0x30, 0x30, 0x00, 0x50, 0x40, 20 | 0x00, 0x00, 0x00, 0x40, 0x26, 0x00, 0x2c, 0x43, 0x16, 0x00, 0x8d, 0x23, 21 | 0x6a, 0x00, 0x60, 0x4d, 0x00, 0x00, 0x00, 0x3e, 0x4e, 0x00, 0x33, 0x4d, 22 | 0x19, 0x00, 0x40, 0x40, 0x40, 0x00, 0x00, 0x41, 0x51, 0x00, 0x00, 0x50, 23 | 0x30, 0x00, 0x70, 0x59, 0x00, 0x00, 0x39, 0x56, 0x1c, 0x00, 0x00, 0x4d, 24 | 0x60, 0x00, 0x40, 0x60, 0x20, 0x00, 0x50, 0x50, 0x50, 0x00, 0x80, 0x66, 25 | 0x00, 0x00, 0x00, 0x60, 0x39, 0x00, 0x00, 0x33, 0xcc, 0x00, 0xcc, 0x33, 26 | 0x99, 0x00, 0x46, 0x6a, 0x23, 0x00, 0x90, 0x73, 0x00, 0x00, 0x23, 0x46, 27 | 0xb0, 0x00, 0x5d, 0x5d, 0x5d, 0x00, 0x00, 0x70, 0x43, 0x00, 0x4d, 0x73, 28 | 0x26, 0x00, 0x60, 0x60, 0x60, 0x00, 0xa0, 0x80, 0x00, 0x00, 0x53, 0x7d, 29 | 0x29, 0x00, 0x00, 0x6c, 0x87, 0x00, 0x00, 0x80, 0x4d, 0x00, 0xb0, 0x8d, 30 | 0x00, 0x00, 0x70, 0x70, 0x70, 0x00, 0x00, 0x75, 0x92, 0x00, 0x00, 0x76, 31 | 0x93, 0x00, 0x00, 0x7a, 0x99, 0x00, 0xc0, 0x9a, 0x00, 0x00, 0x00, 0x90, 32 | 0x56, 0x00, 0x00, 0x7b, 0x9a, 0x00, 0x66, 0x99, 0x33, 0x00, 0x80, 0x80, 33 | 0x80, 0x00, 0xd0, 0xa7, 0x00, 0x00, 0x33, 0x66, 0xff, 0x00, 0x00, 0xa0, 34 | 0x60, 0x00, 0x00, 0x8d, 0xb0, 0x00, 0x00, 0x8e, 0xb1, 0x00, 0x90, 0x90, 35 | 0x90, 0x00, 0x00, 0x93, 0xb8, 0x00, 0x95, 0x95, 0x95, 0x00, 0x00, 0xb0, 36 | 0x6a, 0x00, 0x99, 0x99, 0x99, 0x00, 0x00, 0x9b, 0xc1, 0x00, 0xa0, 0xa0, 37 | 0xa0, 0x00, 0xff, 0xcc, 0x00, 0x00, 0x00, 0xa4, 0xcd, 0x00, 0x00, 0xc0, 38 | 0x73, 0x00, 0x00, 0xa9, 0xd3, 0x00, 0x00, 0xaa, 0xd4, 0x00, 0xb0, 0xb0, 39 | 0xb0, 0x00, 0x00, 0xb1, 0xdd, 0x00, 0x00, 0xd0, 0x7d, 0x00, 0x00, 0xb3, 40 | 0xe0, 0x00, 0x00, 0xc1, 0xf1, 0x00, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0xc4, 41 | 0xf5, 0x00, 0x00, 0xca, 0xfc, 0x00, 0x00, 0xcc, 0xff, 0x00, 0xd0, 0xd0, 42 | 0xd0, 0x00, 0x00, 0xff, 0x99, 0x00, 0xff, 0xff, 0xff, 0x00, 0xf0, 0x00, 43 | 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 44 | 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 45 | 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x22, 0x00, 0x01, 0x24, 0x01, 0x19, 46 | 0x5a, 0x00, 0x00, 0x03, 0x0e, 0x24, 0x19, 0x00, 0x21, 0x00, 0x01, 0x24, 47 | 0x01, 0x19, 0x4c, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x19, 0x01, 0x67, 48 | 0x04, 0x69, 0x01, 0x44, 0x2e, 0x00, 0x01, 0x19, 0x01, 0x67, 0x1a, 0x69, 49 | 0x01, 0x4c, 0x0b, 0x00, 0x01, 0x2c, 0x05, 0x69, 0x01, 0x33, 0x1c, 0x00, 50 | 0x01, 0x19, 0x01, 0x67, 0x04, 0x69, 0x01, 0x44, 0x1b, 0x00, 0x01, 0x19, 51 | 0x01, 0x67, 0x1b, 0x69, 0x01, 0x63, 0x11, 0x00, 0x00, 0x00, 0x1f, 0x00, 52 | 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x58, 0x1c, 0x69, 0x0b, 0x00, 53 | 0x01, 0x5e, 0x05, 0x69, 0x01, 0x67, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 54 | 0x1b, 0x00, 0x01, 0x58, 0x1d, 0x69, 0x01, 0x24, 0x10, 0x00, 0x00, 0x00, 55 | 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x1c, 0x69, 56 | 0x0b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 57 | 0x1b, 0x00, 0x01, 0x5e, 0x1d, 0x69, 0x01, 0x24, 0x10, 0x00, 0x00, 0x00, 58 | 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x1c, 0x69, 59 | 0x0b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 60 | 0x1b, 0x00, 0x01, 0x5e, 0x1d, 0x69, 0x01, 0x24, 0x10, 0x00, 0x00, 0x00, 61 | 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x1c, 0x69, 62 | 0x0b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 63 | 0x1b, 0x00, 0x01, 0x5e, 0x1d, 0x69, 0x01, 0x19, 0x10, 0x00, 0x00, 0x00, 64 | 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 65 | 0x15, 0x5e, 0x01, 0x2c, 0x0b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 66 | 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x16, 0x5e, 67 | 0x01, 0x44, 0x11, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 68 | 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 69 | 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 70 | 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 71 | 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 72 | 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 73 | 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 74 | 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 75 | 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 76 | 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 77 | 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 78 | 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 79 | 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 80 | 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 81 | 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 82 | 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 83 | 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 84 | 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 85 | 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 86 | 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 87 | 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 88 | 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 89 | 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 90 | 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 91 | 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 92 | 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 93 | 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 94 | 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 95 | 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 96 | 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 97 | 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 98 | 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 99 | 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 100 | 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 101 | 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 102 | 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 103 | 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 104 | 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 105 | 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 106 | 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 107 | 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 108 | 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 109 | 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 110 | 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x00, 0x03, 0x05, 0x14, 111 | 0x09, 0x00, 0x10, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 112 | 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 113 | 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 114 | 0x19, 0x00, 0x00, 0x07, 0x07, 0x45, 0x64, 0x66, 0x65, 0x51, 0x11, 0x00, 115 | 0x0e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 116 | 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 117 | 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x18, 0x00, 118 | 0x01, 0x01, 0x01, 0x53, 0x05, 0x66, 0x01, 0x61, 0x01, 0x0b, 0x0d, 0x00, 119 | 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 120 | 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 121 | 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x18, 0x00, 0x01, 0x2a, 122 | 0x07, 0x66, 0x01, 0x41, 0x0d, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 123 | 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 124 | 0x01, 0x5e, 0x1a, 0x69, 0x01, 0x4c, 0x13, 0x00, 0x00, 0x00, 0x1f, 0x00, 125 | 0x01, 0x44, 0x06, 0x69, 0x18, 0x00, 0x01, 0x46, 0x07, 0x66, 0x01, 0x5a, 126 | 0x0d, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 127 | 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x1b, 0x69, 128 | 0x13, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x18, 0x00, 129 | 0x01, 0x47, 0x07, 0x66, 0x01, 0x5c, 0x0d, 0x00, 0x01, 0x5e, 0x06, 0x69, 130 | 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 131 | 0x1b, 0x00, 0x01, 0x5e, 0x1b, 0x69, 0x13, 0x00, 0x00, 0x00, 0x1f, 0x00, 132 | 0x01, 0x44, 0x06, 0x69, 0x18, 0x00, 0x01, 0x31, 0x07, 0x66, 0x01, 0x4a, 133 | 0x0d, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 134 | 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x1b, 0x69, 135 | 0x13, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x18, 0x00, 136 | 0x01, 0x04, 0x01, 0x5d, 0x05, 0x66, 0x01, 0x62, 0x01, 0x16, 0x0d, 0x00, 137 | 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 138 | 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x1a, 0x69, 0x01, 0x67, 139 | 0x13, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x19, 0x00, 140 | 0x01, 0x10, 0x01, 0x57, 0x03, 0x66, 0x01, 0x5f, 0x01, 0x22, 0x0e, 0x00, 141 | 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 142 | 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x14, 0x5e, 143 | 0x01, 0x2c, 0x13, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 144 | 0x1b, 0x00, 0x00, 0x04, 0x1b, 0x2d, 0x23, 0x03, 0x0f, 0x00, 0x01, 0x5e, 145 | 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 146 | 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 147 | 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 148 | 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 149 | 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 150 | 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 151 | 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 152 | 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 153 | 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 154 | 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 155 | 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 156 | 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 157 | 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 158 | 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 159 | 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 160 | 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 161 | 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 162 | 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 163 | 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 164 | 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 165 | 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 166 | 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 167 | 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 168 | 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 169 | 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 170 | 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 171 | 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 172 | 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 173 | 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 174 | 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 175 | 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 176 | 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 177 | 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 178 | 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 179 | 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 180 | 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 181 | 0x1f, 0x00, 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 182 | 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 183 | 0x1b, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 184 | 0x01, 0x44, 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 185 | 0x01, 0x5e, 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 186 | 0x01, 0x5e, 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x44, 187 | 0x06, 0x69, 0x2e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 188 | 0x06, 0x69, 0x1c, 0x00, 0x01, 0x44, 0x06, 0x69, 0x1b, 0x00, 0x01, 0x5e, 189 | 0x06, 0x69, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x0e, 0x0e, 0x24, 190 | 0x01, 0x52, 0x06, 0x69, 0x0e, 0x24, 0x01, 0x19, 0x1f, 0x00, 0x01, 0x5e, 191 | 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x0d, 0x00, 0x01, 0x0e, 192 | 0x0e, 0x24, 0x01, 0x52, 0x06, 0x69, 0x0e, 0x24, 0x01, 0x19, 0x0c, 0x00, 193 | 0x01, 0x5e, 0x06, 0x69, 0x16, 0x24, 0x12, 0x00, 0x00, 0x00, 0x0f, 0x00, 194 | 0x01, 0x24, 0x25, 0x69, 0x01, 0x33, 0x1e, 0x00, 0x01, 0x5e, 0x06, 0x69, 195 | 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x0c, 0x00, 0x01, 0x24, 0x25, 0x69, 196 | 0x01, 0x33, 0x0b, 0x00, 0x01, 0x5e, 0x1d, 0x69, 0x11, 0x00, 0x00, 0x00, 197 | 0x0f, 0x00, 0x01, 0x44, 0x25, 0x69, 0x01, 0x58, 0x1e, 0x00, 0x01, 0x5e, 198 | 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x0c, 0x00, 0x01, 0x44, 199 | 0x25, 0x69, 0x01, 0x58, 0x0b, 0x00, 0x01, 0x5e, 0x1d, 0x69, 0x01, 0x24, 200 | 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x01, 0x44, 0x25, 0x69, 0x01, 0x5e, 201 | 0x1e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 0x01, 0x5e, 0x06, 0x69, 202 | 0x0c, 0x00, 0x01, 0x44, 0x25, 0x69, 0x01, 0x5e, 0x0b, 0x00, 0x01, 0x5e, 203 | 0x1d, 0x69, 0x01, 0x24, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x01, 0x44, 204 | 0x25, 0x69, 0x01, 0x5e, 0x1e, 0x00, 0x01, 0x5e, 0x06, 0x69, 0x21, 0x00, 205 | 0x01, 0x5e, 0x06, 0x69, 0x0c, 0x00, 0x01, 0x44, 0x25, 0x69, 0x01, 0x5e, 206 | 0x0b, 0x00, 0x01, 0x5e, 0x1d, 0x69, 0x01, 0x24, 0x10, 0x00, 0x00, 0x00, 207 | 0x0f, 0x00, 0x01, 0x33, 0x25, 0x69, 0x01, 0x44, 0x1e, 0x00, 0x01, 0x52, 208 | 0x05, 0x69, 0x01, 0x58, 0x21, 0x00, 0x01, 0x52, 0x05, 0x69, 0x01, 0x58, 209 | 0x0c, 0x00, 0x01, 0x33, 0x25, 0x69, 0x01, 0x44, 0x0b, 0x00, 0x01, 0x44, 210 | 0x1d, 0x69, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x3e, 0x23, 0x44, 211 | 0x01, 0x3e, 0x20, 0x00, 0x00, 0x05, 0x3e, 0x52, 0x5e, 0x58, 0x3e, 0x00, 212 | 0x23, 0x00, 0x00, 0x05, 0x3e, 0x52, 0x5e, 0x58, 0x3e, 0x00, 0x0e, 0x00, 213 | 0x01, 0x3e, 0x23, 0x44, 0x01, 0x3e, 0x0d, 0x00, 0x01, 0x33, 0x1b, 0x44, 214 | 0x01, 0x2c, 0x11, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 215 | 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 216 | 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x3b, 217 | 0x54, 0x00, 0xda, 0x56, 0x00, 0x03, 0x54, 0x3b, 0x02, 0x00, 0x08, 0x00, 218 | 0x00, 0x00, 0x08, 0x00, 0x01, 0x21, 0xde, 0x56, 0x01, 0x21, 0x08, 0x00, 219 | 0x00, 0x00, 0x08, 0x00, 0x01, 0x21, 0xde, 0x56, 0x01, 0x21, 0x08, 0x00, 220 | 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x3b, 0x54, 0x00, 0xda, 0x56, 221 | 0x00, 0x03, 0x54, 0x3b, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0xf0, 0x00, 222 | 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 223 | 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 224 | 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 225 | 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 226 | 0x01, 0x50, 0x06, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 227 | 0x09, 0x4b, 0x01, 0x38, 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 228 | 0x05, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x05, 0x00, 0x09, 0x68, 229 | 0x06, 0x00, 0x09, 0x59, 0x01, 0x2f, 0x06, 0x00, 0x01, 0x17, 0x09, 0x59, 230 | 0x01, 0x17, 0x06, 0x00, 0x01, 0x28, 0x13, 0x37, 0x01, 0x1c, 0x0c, 0x00, 231 | 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 232 | 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 233 | 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 0x01, 0x50, 0x06, 0x00, 234 | 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x09, 0x4b, 0x01, 0x38, 235 | 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x05, 0x00, 0x09, 0x68, 236 | 0x01, 0x3c, 0x05, 0x00, 0x09, 0x68, 0x06, 0x00, 0x09, 0x59, 0x01, 0x43, 237 | 0x06, 0x00, 0x01, 0x17, 0x09, 0x59, 0x01, 0x17, 0x06, 0x00, 0x01, 0x28, 238 | 0x13, 0x37, 0x01, 0x1c, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 239 | 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 240 | 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 241 | 0x13, 0x66, 0x01, 0x50, 0x06, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 242 | 0x03, 0x00, 0x09, 0x4b, 0x01, 0x38, 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 243 | 0x01, 0x20, 0x04, 0x00, 0x01, 0x0a, 0x09, 0x68, 0x01, 0x49, 0x05, 0x00, 244 | 0x09, 0x68, 0x06, 0x00, 0x01, 0x4d, 0x08, 0x59, 0x01, 0x43, 0x06, 0x00, 245 | 0x01, 0x1f, 0x09, 0x59, 0x07, 0x00, 0x01, 0x28, 0x13, 0x37, 0x01, 0x1c, 246 | 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 247 | 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 248 | 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 0x01, 0x50, 249 | 0x06, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x09, 0x4b, 250 | 0x01, 0x38, 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x04, 0x00, 251 | 0x01, 0x26, 0x09, 0x68, 0x01, 0x55, 0x05, 0x00, 0x09, 0x68, 0x06, 0x00, 252 | 0x01, 0x43, 0x08, 0x59, 0x01, 0x43, 0x06, 0x00, 0x01, 0x2f, 0x09, 0x59, 253 | 0x07, 0x00, 0x01, 0x28, 0x13, 0x37, 0x01, 0x1c, 0x0c, 0x00, 0x00, 0x00, 254 | 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 255 | 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 256 | 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 0x01, 0x50, 0x06, 0x00, 0x01, 0x38, 257 | 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x09, 0x4b, 0x01, 0x38, 0x06, 0x00, 258 | 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x04, 0x00, 0x01, 0x3c, 0x0a, 0x68, 259 | 0x05, 0x00, 0x09, 0x68, 0x06, 0x00, 0x01, 0x43, 0x08, 0x59, 0x01, 0x4d, 260 | 0x06, 0x00, 0x01, 0x2f, 0x08, 0x59, 0x01, 0x4d, 0x07, 0x00, 0x01, 0x28, 261 | 0x13, 0x37, 0x01, 0x1c, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 262 | 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 263 | 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 264 | 0x13, 0x66, 0x01, 0x50, 0x06, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 265 | 0x03, 0x00, 0x09, 0x4b, 0x01, 0x38, 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 266 | 0x01, 0x20, 0x04, 0x00, 0x01, 0x4f, 0x0a, 0x68, 0x05, 0x00, 0x09, 0x68, 267 | 0x06, 0x00, 0x01, 0x34, 0x09, 0x59, 0x06, 0x00, 0x01, 0x34, 0x08, 0x59, 268 | 0x01, 0x43, 0x07, 0x00, 0x01, 0x28, 0x13, 0x37, 0x01, 0x1c, 0x0c, 0x00, 269 | 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 270 | 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 271 | 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 0x01, 0x50, 0x06, 0x00, 272 | 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x09, 0x4b, 0x01, 0x38, 273 | 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x04, 0x00, 0x01, 0x5b, 274 | 0x0a, 0x68, 0x01, 0x20, 0x04, 0x00, 0x09, 0x68, 0x06, 0x00, 0x01, 0x2f, 275 | 0x09, 0x59, 0x06, 0x00, 0x01, 0x43, 0x08, 0x59, 0x01, 0x43, 0x07, 0x00, 276 | 0x01, 0x28, 0x13, 0x37, 0x01, 0x1c, 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 277 | 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 278 | 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 279 | 0x01, 0x50, 0x13, 0x66, 0x01, 0x50, 0x06, 0x00, 0x01, 0x38, 0x09, 0x4b, 280 | 0x01, 0x12, 0x03, 0x00, 0x09, 0x4b, 0x01, 0x38, 0x06, 0x00, 0x01, 0x55, 281 | 0x08, 0x68, 0x01, 0x20, 0x04, 0x00, 0x0b, 0x68, 0x01, 0x35, 0x04, 0x00, 282 | 0x09, 0x68, 0x06, 0x00, 0x01, 0x29, 0x09, 0x59, 0x06, 0x00, 0x01, 0x43, 283 | 0x08, 0x59, 0x01, 0x34, 0x07, 0x00, 0x01, 0x28, 0x13, 0x37, 0x01, 0x1c, 284 | 0x0c, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 285 | 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 286 | 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 0x01, 0x50, 287 | 0x06, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x09, 0x4b, 288 | 0x01, 0x38, 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x03, 0x00, 289 | 0x01, 0x15, 0x0b, 0x68, 0x01, 0x42, 0x04, 0x00, 0x09, 0x68, 0x06, 0x00, 290 | 0x01, 0x17, 0x09, 0x59, 0x01, 0x48, 0x05, 0x43, 0x09, 0x59, 0x01, 0x2f, 291 | 0x07, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 292 | 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 293 | 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 294 | 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 0x01, 0x50, 0x06, 0x00, 0x01, 0x38, 295 | 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x09, 0x4b, 0x01, 0x38, 0x06, 0x00, 296 | 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x03, 0x00, 0x01, 0x26, 0x0b, 0x68, 297 | 0x01, 0x55, 0x04, 0x00, 0x09, 0x68, 0x06, 0x00, 0x01, 0x17, 0x18, 0x59, 298 | 0x01, 0x29, 0x07, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 299 | 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 300 | 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 301 | 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 302 | 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x09, 0x4b, 0x01, 0x38, 303 | 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x03, 0x00, 0x01, 0x3c, 304 | 0x0b, 0x68, 0x01, 0x60, 0x04, 0x00, 0x09, 0x68, 0x06, 0x00, 0x01, 0x08, 305 | 0x18, 0x59, 0x01, 0x17, 0x07, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 306 | 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 307 | 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 308 | 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 309 | 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x0a, 0x4b, 310 | 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x03, 0x00, 0x01, 0x4f, 311 | 0x0c, 0x68, 0x04, 0x00, 0x09, 0x68, 0x07, 0x00, 0x18, 0x59, 0x01, 0x17, 312 | 0x07, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 313 | 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 314 | 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 315 | 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 0x01, 0x38, 316 | 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x0a, 0x4b, 0x06, 0x00, 0x01, 0x55, 317 | 0x08, 0x68, 0x01, 0x20, 0x03, 0x00, 0x01, 0x5b, 0x0c, 0x68, 0x01, 0x20, 318 | 0x03, 0x00, 0x09, 0x68, 0x07, 0x00, 0x18, 0x59, 0x01, 0x08, 0x07, 0x00, 319 | 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 320 | 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 321 | 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 322 | 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 323 | 0x01, 0x12, 0x03, 0x00, 0x0a, 0x4b, 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 324 | 0x01, 0x20, 0x03, 0x00, 0x0d, 0x68, 0x01, 0x2e, 0x03, 0x00, 0x09, 0x68, 325 | 0x07, 0x00, 0x01, 0x43, 0x17, 0x59, 0x08, 0x00, 0x01, 0x28, 0x09, 0x37, 326 | 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 327 | 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 328 | 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 329 | 0x01, 0x1d, 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 330 | 0x0a, 0x4b, 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 0x00, 0x04, 0x20, 0x00, 331 | 0x00, 0x15, 0x0d, 0x68, 0x01, 0x3c, 0x03, 0x00, 0x09, 0x68, 0x07, 0x00, 332 | 0x01, 0x43, 0x17, 0x59, 0x08, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 333 | 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 334 | 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 335 | 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 336 | 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x0a, 0x4b, 337 | 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 0x00, 0x04, 0x20, 0x00, 0x00, 0x26, 338 | 0x0d, 0x68, 0x01, 0x55, 0x03, 0x00, 0x09, 0x68, 0x07, 0x00, 0x01, 0x39, 339 | 0x16, 0x59, 0x01, 0x48, 0x08, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 340 | 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 341 | 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 342 | 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 343 | 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x0a, 0x4b, 344 | 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 0x00, 0x04, 0x20, 0x00, 0x00, 0x3c, 345 | 0x0d, 0x68, 0x01, 0x5b, 0x03, 0x00, 0x09, 0x68, 0x07, 0x00, 0x01, 0x2f, 346 | 0x09, 0x59, 0x04, 0x00, 0x01, 0x3f, 0x08, 0x59, 0x01, 0x43, 0x08, 0x00, 347 | 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 348 | 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 349 | 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 350 | 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 351 | 0x01, 0x12, 0x03, 0x00, 0x0a, 0x4b, 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 352 | 0x00, 0x04, 0x20, 0x00, 0x00, 0x4f, 0x0e, 0x68, 0x03, 0x00, 0x09, 0x68, 353 | 0x07, 0x00, 0x01, 0x2f, 0x09, 0x59, 0x04, 0x00, 0x01, 0x43, 0x08, 0x59, 354 | 0x01, 0x3f, 0x08, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 355 | 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 356 | 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 357 | 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 358 | 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x0a, 0x4b, 0x06, 0x00, 359 | 0x01, 0x55, 0x08, 0x68, 0x00, 0x04, 0x20, 0x00, 0x00, 0x5b, 0x0e, 0x68, 360 | 0x00, 0x03, 0x15, 0x00, 0x00, 0x00, 0x09, 0x68, 0x07, 0x00, 0x01, 0x1f, 361 | 0x09, 0x59, 0x04, 0x00, 0x01, 0x43, 0x08, 0x59, 0x01, 0x2f, 0x08, 0x00, 362 | 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 363 | 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 364 | 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 365 | 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 366 | 0x01, 0x12, 0x03, 0x00, 0x0a, 0x4b, 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 367 | 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x0f, 0x68, 0x00, 0x03, 0x26, 0x00, 368 | 0x00, 0x00, 0x09, 0x68, 0x07, 0x00, 0x01, 0x17, 0x09, 0x59, 0x01, 0x0f, 369 | 0x03, 0x00, 0x09, 0x59, 0x01, 0x2f, 0x08, 0x00, 0x01, 0x28, 0x09, 0x37, 370 | 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 371 | 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 372 | 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 373 | 0x01, 0x1d, 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 374 | 0x09, 0x4b, 0x01, 0x40, 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 0x00, 0x03, 375 | 0x20, 0x00, 0x20, 0x00, 0x07, 0x68, 0x01, 0x55, 0x07, 0x68, 0x00, 0x03, 376 | 0x3c, 0x00, 0x00, 0x00, 0x09, 0x68, 0x07, 0x00, 0x01, 0x0f, 0x09, 0x59, 377 | 0x01, 0x17, 0x03, 0x00, 0x09, 0x59, 0x01, 0x25, 0x08, 0x00, 0x01, 0x28, 378 | 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 379 | 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 380 | 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 381 | 0x12, 0x66, 0x01, 0x50, 0x07, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x00, 0x04, 382 | 0x12, 0x00, 0x00, 0x06, 0x09, 0x4b, 0x01, 0x38, 0x06, 0x00, 0x01, 0x55, 383 | 0x08, 0x68, 0x00, 0x03, 0x20, 0x00, 0x2e, 0x00, 0x07, 0x68, 0x01, 0x35, 384 | 0x07, 0x68, 0x00, 0x03, 0x4f, 0x00, 0x00, 0x00, 0x09, 0x68, 0x08, 0x00, 385 | 0x09, 0x59, 0x01, 0x17, 0x03, 0x00, 0x09, 0x59, 0x01, 0x17, 0x08, 0x00, 386 | 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 387 | 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 388 | 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 389 | 0x01, 0x50, 0x12, 0x66, 0x01, 0x50, 0x07, 0x00, 0x01, 0x38, 0x09, 0x4b, 390 | 0x00, 0x04, 0x12, 0x00, 0x18, 0x3d, 0x09, 0x4b, 0x01, 0x2b, 0x06, 0x00, 391 | 0x01, 0x55, 0x08, 0x68, 0x00, 0x03, 0x20, 0x00, 0x3c, 0x00, 0x07, 0x68, 392 | 0x01, 0x15, 0x07, 0x68, 0x00, 0x03, 0x55, 0x00, 0x00, 0x00, 0x09, 0x68, 393 | 0x08, 0x00, 0x09, 0x59, 0x00, 0x04, 0x25, 0x00, 0x00, 0x17, 0x09, 0x59, 394 | 0x01, 0x17, 0x08, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 395 | 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 396 | 0x01, 0x3a, 0x17, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x12, 0x66, 397 | 0x01, 0x50, 0x07, 0x00, 0x01, 0x38, 0x16, 0x4b, 0x01, 0x0d, 0x06, 0x00, 398 | 0x01, 0x55, 0x08, 0x68, 0x00, 0x03, 0x20, 0x00, 0x55, 0x00, 0x07, 0x68, 399 | 0x01, 0x00, 0x01, 0x60, 0x07, 0x68, 0x01, 0x00, 0x01, 0x00, 0x09, 0x68, 400 | 0x08, 0x00, 0x01, 0x4d, 0x08, 0x59, 0x00, 0x04, 0x2f, 0x00, 0x00, 0x17, 401 | 0x09, 0x59, 0x09, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 402 | 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 403 | 0x01, 0x3a, 0x17, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x12, 0x66, 404 | 0x01, 0x50, 0x07, 0x00, 0x01, 0x38, 0x15, 0x4b, 0x01, 0x30, 0x07, 0x00, 405 | 0x01, 0x55, 0x08, 0x68, 0x00, 0x03, 0x20, 0x00, 0x60, 0x00, 0x06, 0x68, 406 | 0x00, 0x03, 0x60, 0x00, 0x55, 0x00, 0x07, 0x68, 0x01, 0x0a, 0x01, 0x00, 407 | 0x09, 0x68, 0x08, 0x00, 0x01, 0x43, 0x08, 0x59, 0x00, 0x04, 0x2f, 0x00, 408 | 0x00, 0x17, 0x09, 0x59, 0x09, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 409 | 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 410 | 0x0b, 0x00, 0x01, 0x3a, 0x17, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 411 | 0x12, 0x66, 0x01, 0x50, 0x07, 0x00, 0x01, 0x38, 0x14, 0x4b, 0x01, 0x2b, 412 | 0x08, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x01, 0x00, 0x07, 0x68, 413 | 0x00, 0x03, 0x55, 0x00, 0x42, 0x00, 0x07, 0x68, 0x01, 0x20, 0x01, 0x00, 414 | 0x09, 0x68, 0x08, 0x00, 0x01, 0x43, 0x08, 0x59, 0x00, 0x04, 0x2f, 0x00, 415 | 0x00, 0x2f, 0x09, 0x59, 0x09, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 416 | 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 417 | 0x0b, 0x00, 0x01, 0x3a, 0x17, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 418 | 0x12, 0x66, 0x01, 0x50, 0x07, 0x00, 0x01, 0x38, 0x13, 0x4b, 0x01, 0x1e, 419 | 0x09, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x01, 0x20, 0x07, 0x68, 420 | 0x00, 0x03, 0x49, 0x00, 0x3c, 0x00, 0x07, 0x68, 0x01, 0x3c, 0x01, 0x00, 421 | 0x09, 0x68, 0x08, 0x00, 0x01, 0x2f, 0x08, 0x59, 0x00, 0x04, 0x43, 0x00, 422 | 0x00, 0x2f, 0x08, 0x59, 0x01, 0x43, 0x09, 0x00, 0x01, 0x28, 0x09, 0x37, 423 | 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 424 | 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x17, 0x4e, 0x01, 0x3a, 0x07, 0x00, 425 | 0x01, 0x50, 0x12, 0x66, 0x01, 0x50, 0x07, 0x00, 0x01, 0x38, 0x14, 0x4b, 426 | 0x01, 0x32, 0x08, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x01, 0x2e, 427 | 0x07, 0x68, 0x00, 0x03, 0x3c, 0x00, 0x26, 0x00, 0x07, 0x68, 0x01, 0x42, 428 | 0x01, 0x00, 0x09, 0x68, 0x08, 0x00, 0x01, 0x2f, 0x08, 0x59, 0x00, 0x04, 429 | 0x43, 0x00, 0x00, 0x2f, 0x08, 0x59, 0x01, 0x43, 0x09, 0x00, 0x01, 0x28, 430 | 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 431 | 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x17, 0x4e, 0x01, 0x3a, 432 | 0x07, 0x00, 0x01, 0x50, 0x12, 0x66, 0x01, 0x50, 0x07, 0x00, 0x01, 0x38, 433 | 0x15, 0x4b, 0x01, 0x38, 0x07, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 434 | 0x01, 0x3c, 0x07, 0x68, 0x00, 0x03, 0x26, 0x00, 0x20, 0x00, 0x07, 0x68, 435 | 0x01, 0x55, 0x01, 0x00, 0x09, 0x68, 0x08, 0x00, 0x01, 0x25, 0x08, 0x59, 436 | 0x00, 0x04, 0x43, 0x00, 0x00, 0x43, 0x08, 0x59, 0x01, 0x34, 0x09, 0x00, 437 | 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 438 | 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x17, 0x4e, 439 | 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x12, 0x66, 0x01, 0x50, 0x07, 0x00, 440 | 0x01, 0x38, 0x16, 0x4b, 0x01, 0x1e, 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 441 | 0x01, 0x20, 0x01, 0x55, 0x07, 0x68, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 442 | 0x08, 0x68, 0x01, 0x00, 0x09, 0x68, 0x08, 0x00, 0x01, 0x17, 0x08, 0x59, 443 | 0x00, 0x04, 0x4d, 0x00, 0x00, 0x43, 0x08, 0x59, 0x01, 0x2f, 0x09, 0x00, 444 | 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 445 | 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x17, 0x4e, 446 | 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 447 | 0x01, 0x38, 0x16, 0x4b, 0x01, 0x3d, 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 448 | 0x01, 0x20, 0x01, 0x60, 0x07, 0x68, 0x03, 0x00, 0x08, 0x68, 0x01, 0x00, 449 | 0x09, 0x68, 0x08, 0x00, 0x01, 0x17, 0x09, 0x59, 0x00, 0x03, 0x00, 0x00, 450 | 0x48, 0x00, 0x08, 0x59, 0x01, 0x2f, 0x09, 0x00, 0x01, 0x28, 0x09, 0x37, 451 | 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 452 | 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x17, 0x4e, 0x01, 0x3a, 0x07, 0x00, 453 | 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 0x01, 0x38, 0x17, 0x4b, 454 | 0x06, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x20, 0x08, 0x68, 0x03, 0x00, 455 | 0x01, 0x55, 0x07, 0x68, 0x01, 0x20, 0x09, 0x68, 0x09, 0x00, 0x09, 0x59, 456 | 0x01, 0x00, 0x01, 0x00, 0x09, 0x59, 0x01, 0x17, 0x09, 0x00, 0x01, 0x28, 457 | 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 458 | 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 459 | 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 460 | 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x00, 0x04, 461 | 0x12, 0x00, 0x00, 0x12, 0x0a, 0x4b, 0x01, 0x12, 0x05, 0x00, 0x01, 0x55, 462 | 0x08, 0x68, 0x01, 0x3c, 0x07, 0x68, 0x01, 0x55, 0x03, 0x00, 0x01, 0x49, 463 | 0x07, 0x68, 0x01, 0x35, 0x09, 0x68, 0x09, 0x00, 0x09, 0x59, 0x01, 0x00, 464 | 0x01, 0x00, 0x09, 0x59, 0x01, 0x17, 0x09, 0x00, 0x01, 0x28, 0x09, 0x37, 465 | 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 466 | 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 467 | 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 468 | 0x01, 0x1d, 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 469 | 0x01, 0x2b, 0x09, 0x4b, 0x01, 0x27, 0x05, 0x00, 0x01, 0x55, 0x08, 0x68, 470 | 0x01, 0x49, 0x07, 0x68, 0x01, 0x4f, 0x03, 0x00, 0x01, 0x3c, 0x07, 0x68, 471 | 0x01, 0x3c, 0x09, 0x68, 0x09, 0x00, 0x09, 0x59, 0x01, 0x17, 0x01, 0x00, 472 | 0x09, 0x59, 0x01, 0x08, 0x09, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 473 | 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 474 | 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 475 | 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 476 | 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x01, 0x18, 477 | 0x09, 0x4b, 0x01, 0x27, 0x05, 0x00, 0x01, 0x55, 0x08, 0x68, 0x01, 0x5b, 478 | 0x07, 0x68, 0x01, 0x3c, 0x03, 0x00, 0x01, 0x2e, 0x07, 0x68, 0x01, 0x55, 479 | 0x09, 0x68, 0x09, 0x00, 0x01, 0x43, 0x08, 0x59, 0x01, 0x17, 0x01, 0x17, 480 | 0x09, 0x59, 0x0a, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 481 | 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 482 | 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 483 | 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 484 | 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x01, 0x12, 0x09, 0x4b, 485 | 0x01, 0x27, 0x05, 0x00, 0x01, 0x55, 0x10, 0x68, 0x01, 0x2e, 0x03, 0x00, 486 | 0x01, 0x20, 0x07, 0x68, 0x01, 0x60, 0x09, 0x68, 0x09, 0x00, 0x01, 0x43, 487 | 0x08, 0x59, 0x01, 0x17, 0x01, 0x17, 0x09, 0x59, 0x0a, 0x00, 0x01, 0x28, 488 | 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 489 | 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 490 | 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 491 | 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 492 | 0x03, 0x00, 0x01, 0x12, 0x09, 0x4b, 0x01, 0x27, 0x05, 0x00, 0x01, 0x55, 493 | 0x10, 0x68, 0x01, 0x20, 0x04, 0x00, 0x11, 0x68, 0x09, 0x00, 0x01, 0x34, 494 | 0x08, 0x59, 0x01, 0x29, 0x01, 0x25, 0x08, 0x59, 0x01, 0x48, 0x0a, 0x00, 495 | 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x12, 0x00, 496 | 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 0x09, 0x4e, 497 | 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 498 | 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 0x01, 0x38, 0x09, 0x4b, 499 | 0x01, 0x12, 0x03, 0x00, 0x01, 0x12, 0x09, 0x4b, 0x01, 0x27, 0x05, 0x00, 500 | 0x01, 0x55, 0x10, 0x68, 0x01, 0x0a, 0x04, 0x00, 0x11, 0x68, 0x09, 0x00, 501 | 0x01, 0x2f, 0x08, 0x59, 0x01, 0x2f, 0x01, 0x2f, 0x08, 0x59, 0x01, 0x43, 502 | 0x0a, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 503 | 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 0x01, 0x3a, 504 | 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 505 | 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 0x01, 0x38, 506 | 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x01, 0x12, 0x09, 0x4b, 0x01, 0x27, 507 | 0x05, 0x00, 0x01, 0x55, 0x10, 0x68, 0x05, 0x00, 0x01, 0x55, 0x10, 0x68, 508 | 0x09, 0x00, 0x01, 0x2f, 0x08, 0x59, 0x01, 0x2f, 0x01, 0x2f, 0x08, 0x59, 509 | 0x01, 0x3f, 0x0a, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 510 | 0x00, 0x00, 0x12, 0x00, 0x01, 0x1a, 0x09, 0x36, 0x01, 0x1a, 0x0b, 0x00, 511 | 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 512 | 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x09, 0x66, 0x01, 0x1d, 0x10, 0x00, 513 | 0x01, 0x38, 0x09, 0x4b, 0x01, 0x12, 0x03, 0x00, 0x01, 0x1e, 0x09, 0x4b, 514 | 0x01, 0x27, 0x05, 0x00, 0x01, 0x55, 0x0f, 0x68, 0x01, 0x5b, 0x05, 0x00, 515 | 0x01, 0x55, 0x10, 0x68, 0x09, 0x00, 0x01, 0x17, 0x08, 0x59, 0x01, 0x34, 516 | 0x01, 0x39, 0x08, 0x59, 0x01, 0x2f, 0x0a, 0x00, 0x01, 0x28, 0x09, 0x37, 517 | 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x1a, 0x15, 0x36, 518 | 0x01, 0x1a, 0x05, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 519 | 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 520 | 0x01, 0x1d, 0x06, 0x00, 0x01, 0x38, 0x09, 0x4b, 0x00, 0x05, 0x12, 0x00, 521 | 0x00, 0x06, 0x3d, 0x00, 0x09, 0x4b, 0x01, 0x1e, 0x05, 0x00, 0x01, 0x55, 522 | 0x0f, 0x68, 0x01, 0x55, 0x05, 0x00, 0x01, 0x3c, 0x10, 0x68, 0x09, 0x00, 523 | 0x01, 0x17, 0x08, 0x59, 0x01, 0x43, 0x01, 0x43, 0x08, 0x59, 0x01, 0x2f, 524 | 0x0a, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 525 | 0x0c, 0x00, 0x01, 0x1a, 0x15, 0x36, 0x01, 0x1a, 0x05, 0x00, 0x01, 0x3a, 526 | 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 527 | 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 0x01, 0x1d, 0x06, 0x00, 0x01, 0x38, 528 | 0x17, 0x4b, 0x01, 0x12, 0x05, 0x00, 0x01, 0x55, 0x0f, 0x68, 0x01, 0x3c, 529 | 0x05, 0x00, 0x01, 0x35, 0x10, 0x68, 0x09, 0x00, 0x01, 0x08, 0x08, 0x59, 530 | 0x01, 0x4d, 0x01, 0x4d, 0x08, 0x59, 0x01, 0x25, 0x0a, 0x00, 0x01, 0x28, 531 | 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x1a, 532 | 0x15, 0x36, 0x01, 0x1a, 0x05, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 533 | 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 534 | 0x13, 0x66, 0x01, 0x1d, 0x06, 0x00, 0x01, 0x38, 0x17, 0x4b, 0x01, 0x06, 535 | 0x05, 0x00, 0x01, 0x55, 0x0f, 0x68, 0x01, 0x3c, 0x05, 0x00, 0x01, 0x20, 536 | 0x10, 0x68, 0x0a, 0x00, 0x12, 0x59, 0x01, 0x17, 0x0a, 0x00, 0x01, 0x28, 537 | 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x1a, 538 | 0x15, 0x36, 0x01, 0x1a, 0x05, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 539 | 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 540 | 0x13, 0x66, 0x01, 0x1d, 0x06, 0x00, 0x01, 0x38, 0x17, 0x4b, 0x06, 0x00, 541 | 0x01, 0x55, 0x0f, 0x68, 0x01, 0x20, 0x05, 0x00, 0x01, 0x0a, 0x10, 0x68, 542 | 0x0a, 0x00, 0x12, 0x59, 0x01, 0x17, 0x0a, 0x00, 0x01, 0x28, 0x09, 0x37, 543 | 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x1a, 0x15, 0x36, 544 | 0x01, 0x1a, 0x05, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 545 | 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 546 | 0x01, 0x1d, 0x06, 0x00, 0x01, 0x38, 0x16, 0x4b, 0x01, 0x32, 0x06, 0x00, 547 | 0x01, 0x55, 0x0f, 0x68, 0x01, 0x15, 0x06, 0x00, 0x10, 0x68, 0x0a, 0x00, 548 | 0x01, 0x48, 0x11, 0x59, 0x0b, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 549 | 0x16, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x1a, 0x15, 0x36, 0x01, 0x1a, 550 | 0x05, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 551 | 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 0x01, 0x1d, 552 | 0x06, 0x00, 0x01, 0x38, 0x16, 0x4b, 0x01, 0x12, 0x06, 0x00, 0x01, 0x55, 553 | 0x0f, 0x68, 0x07, 0x00, 0x01, 0x60, 0x0f, 0x68, 0x0a, 0x00, 0x01, 0x43, 554 | 0x11, 0x59, 0x0b, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 555 | 0x00, 0x00, 0x0c, 0x00, 0x01, 0x1a, 0x15, 0x36, 0x01, 0x1a, 0x05, 0x00, 556 | 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 557 | 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 0x01, 0x1d, 0x06, 0x00, 558 | 0x01, 0x38, 0x15, 0x4b, 0x01, 0x38, 0x07, 0x00, 0x01, 0x55, 0x0e, 0x68, 559 | 0x01, 0x60, 0x07, 0x00, 0x01, 0x55, 0x0f, 0x68, 0x0a, 0x00, 0x01, 0x3f, 560 | 0x11, 0x59, 0x0b, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 561 | 0x00, 0x00, 0x0c, 0x00, 0x01, 0x1a, 0x15, 0x36, 0x01, 0x1a, 0x05, 0x00, 562 | 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 563 | 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 0x01, 0x1d, 0x06, 0x00, 564 | 0x01, 0x38, 0x15, 0x4b, 0x01, 0x06, 0x07, 0x00, 0x01, 0x55, 0x0e, 0x68, 565 | 0x01, 0x55, 0x07, 0x00, 0x01, 0x42, 0x0f, 0x68, 0x0a, 0x00, 0x01, 0x2f, 566 | 0x10, 0x59, 0x01, 0x43, 0x0b, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 567 | 0x16, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x1a, 0x15, 0x36, 0x01, 0x1a, 568 | 0x05, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 0x03, 0x00, 0x01, 0x13, 569 | 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 0x13, 0x66, 0x01, 0x1d, 570 | 0x06, 0x00, 0x01, 0x38, 0x13, 0x4b, 0x01, 0x3d, 0x01, 0x0d, 0x08, 0x00, 571 | 0x01, 0x55, 0x0e, 0x68, 0x01, 0x49, 0x07, 0x00, 0x01, 0x3c, 0x0f, 0x68, 572 | 0x0a, 0x00, 0x01, 0x2f, 0x10, 0x59, 0x01, 0x43, 0x0b, 0x00, 0x01, 0x28, 573 | 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x01, 0x1a, 574 | 0x15, 0x36, 0x01, 0x1a, 0x05, 0x00, 0x01, 0x3a, 0x09, 0x4e, 0x01, 0x13, 575 | 0x03, 0x00, 0x01, 0x13, 0x09, 0x4e, 0x01, 0x3a, 0x07, 0x00, 0x01, 0x50, 576 | 0x13, 0x66, 0x01, 0x1d, 0x06, 0x00, 0x01, 0x38, 0x10, 0x4b, 0x00, 0x03, 577 | 0x40, 0x32, 0x1e, 0x00, 0x0a, 0x00, 0x01, 0x55, 0x0e, 0x68, 0x01, 0x3c, 578 | 0x07, 0x00, 0x01, 0x20, 0x0f, 0x68, 0x0a, 0x00, 0x01, 0x1f, 0x10, 0x59, 579 | 0x01, 0x34, 0x0b, 0x00, 0x01, 0x28, 0x09, 0x37, 0x01, 0x0c, 0x16, 0x00, 580 | 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 581 | 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 582 | 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x01, 583 | }; 584 | -------------------------------------------------------------------------------- /src/mlx90640.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "mlx90640.hpp" 3 | 4 | #include "i2c_master.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // 不良ピクセルのデバッグのために疑似的に設定した不良ピクセルの番号; 12 | // #define DEBUG_BROKENPIXEL 100 13 | 14 | namespace m5 { 15 | static constexpr const uint8_t noise_tbl[] = { 16 | 0, 0, 0, 1, 2, 5, 8, 13, 20, 28, 39, 52, 67, 86, 107, 132, 160, 17 | 0, 0, 0, 1, 3, 5, 9, 14, 20, 29, 39, 52, 68, 86, 108, 132, 160, 18 | 0, 0, 1, 2, 3, 6, 9, 14, 21, 30, 41, 54, 69, 88, 109, 134, 162, 19 | 1, 1, 1, 2, 4, 7, 11, 16, 23, 32, 42, 56, 72, 90, 112, 137, 165, 20 | 1, 2, 2, 3, 5, 8, 12, 18, 25, 34, 45, 59, 75, 94, 116, 141, 170, 21 | 3, 3, 4, 5, 7, 10, 15, 21, 28, 37, 49, 63, 79, 98, 121, 146, 175, 22 | 4, 5, 6, 7, 10, 13, 18, 24, 32, 42, 54, 68, 85, 104, 127, 153, 182, 23 | 7, 7, 8, 10, 13, 17, 22, 28, 37, 47, 59, 74, 91, 111, 134, 161, 191, 24 | 11, 11, 12, 14, 17, 21, 27, 34, 42, 53, 66, 81, 99, 119, 143, 170, 200, 25 | 15, 15, 17, 19, 22, 27, 33, 40, 49, 60, 74, 89, 108, 129, 153, 181, 212, 26 | 21, 21, 22, 25, 29, 33, 40, 48, 57, 69, 83, 99, 118, 140, 165, 193, 225, 27 | 27, 28, 29, 32, 36, 41, 48, 56, 67, 79, 93, 110, 130, 152, 178, 207, 239, 28 | 35, 36, 38, 41, 45, 51, 58, 67, 77, 90, 105, 123, 143, 166, 193, 222, 255}; 29 | 30 | inline uint16_t bswap16(uint16_t data) { 31 | return (data << 8) + (data >> 8); 32 | } 33 | 34 | static constexpr float SCALEALPHA = 0.000001; 35 | static constexpr size_t TA_SHIFT = 8; // Default shift for MLX90640 in open air 36 | // static constexpr size_t COLS = 32; 37 | // static constexpr size_t ROWS = 24; 38 | 39 | struct MLX90640_params_t { 40 | int16_t kVdd; 41 | int16_t vdd25; 42 | float KvPTAT; 43 | float KtPTAT; 44 | uint16_t vPTAT25; 45 | float alphaPTAT; 46 | int16_t gainEE; 47 | float tgc; 48 | float cpKv; 49 | float cpKta; 50 | uint8_t resolutionEE; 51 | uint8_t calibrationModeEE; 52 | float KsTa; 53 | float ksTo[5]; 54 | int16_t ct[5]; 55 | uint16_t alpha[768]; 56 | uint8_t alphaScale; 57 | int16_t offset[768]; 58 | int8_t kta[768]; 59 | uint8_t ktaScale; 60 | int8_t kv[768]; 61 | uint8_t kvScale; 62 | float cpAlpha[2]; 63 | int16_t cpOffset[2]; 64 | float ilChessC[3]; 65 | uint16_t brokenPixels[5]; 66 | uint16_t outlierPixels[5]; 67 | 68 | static int CheckAdjacentPixels(uint16_t pix1, uint16_t pix2) { 69 | int pixPosDif = pix1 - pix2; 70 | if (pixPosDif > -34 && pixPosDif < -30) { 71 | return -6; 72 | } 73 | if (pixPosDif > -2 && pixPosDif < 2) { 74 | return -6; 75 | } 76 | if (pixPosDif > 30 && pixPosDif < 34) { 77 | return -6; 78 | } 79 | return 0; 80 | } 81 | 82 | void setVDDParameters(const uint16_t *eeData) { 83 | int tmp = eeData[0x33] >> 8; // vdd reg 0x2433 84 | if (tmp > 127) { 85 | tmp -= 256; 86 | } 87 | this->kVdd = tmp << 5; 88 | this->vdd25 = (((eeData[0x33] & 0xFF) - 256) << 5) - 8192; 89 | } 90 | 91 | void setPTATParameters(const uint16_t *eeData) { 92 | int tmp = eeData[0x32] >> 10; // PTA reg 0x2432 93 | if (tmp > 31) { 94 | tmp -= 64; 95 | } 96 | this->KvPTAT = (float)tmp / 4096.0f; 97 | 98 | tmp = eeData[0x32] & 0x03FF; 99 | if (tmp > 511) { 100 | tmp -= 1024; 101 | } 102 | this->KtPTAT = (float)tmp / 8.0f; 103 | 104 | this->vPTAT25 = eeData[0x31]; // reg 0x2431 105 | this->alphaPTAT = (float)(eeData[0x10] >> 12) / 4 + 8.0f; // reg 0x2410 106 | } 107 | 108 | void setGainParameters(const uint16_t *eeData) { 109 | int tmp = eeData[0x30]; // reg 0x2430 110 | if (tmp > 32767) { 111 | tmp -= 65536; 112 | } 113 | this->gainEE = tmp; 114 | } 115 | 116 | void setTgcParameters(const uint16_t *eeData) { 117 | int tmp = eeData[0x3C] & 0xFF; // reg 0x243C 118 | if (tmp > 127) { 119 | tmp -= 256; 120 | } 121 | this->tgc = (float)tmp / 32.0f; 122 | } 123 | 124 | void setResolutionParameters(const uint16_t *eeData) { 125 | this->resolutionEE = (eeData[0x38] >> 12) & 0x3; // reg 0x2438 126 | } 127 | 128 | void setKsTaParameters(const uint16_t *eeData) { 129 | int tmp = eeData[0x3C] >> 8; // reg 0x243C 130 | if (tmp > 127) { 131 | tmp -= 256; 132 | } 133 | this->KsTa = (float)tmp / 8192.0f; 134 | } 135 | 136 | void setKsToParameters(const uint16_t *eeData) { 137 | this->ksTo[0] = eeData[0x3D] & 0xFF; // reg 0x243D 138 | this->ksTo[1] = eeData[0x3D] >> 8; 139 | this->ksTo[2] = eeData[0x3E] & 0xFF; // reg 0x243E 140 | this->ksTo[3] = eeData[0x3E] >> 8; 141 | this->ksTo[4] = -0.0002; 142 | 143 | int step = ((eeData[0x3F] >> 12) & 0x3) * 10; 144 | 145 | this->ct[0] = -40; 146 | this->ct[1] = 0; 147 | this->ct[2] = ((eeData[0x3F] >> 4) & 0x0F) * step; 148 | this->ct[3] = ((eeData[0x3F] >> 8) & 0x0F) * step + this->ct[2]; 149 | this->ct[4] = 400; 150 | 151 | int KsToScale = 1 << ((eeData[0x3F] & 0x0F) + 8); 152 | 153 | for (int i = 0; i < 4; i++) { 154 | float tmp = this->ksTo[i]; 155 | if (tmp > 127) { 156 | tmp -= 256; 157 | } 158 | this->ksTo[i] = tmp / KsToScale; 159 | } 160 | } 161 | 162 | void setCPParameters(const uint16_t *eeData) { 163 | float alphaSP[2]; 164 | int16_t offsetSP[2]; 165 | float cpKv; 166 | float cpKta; 167 | uint8_t alphaScale; 168 | uint8_t ktaScale1; 169 | uint8_t kvScale; 170 | 171 | alphaScale = ((eeData[32] & 0xF000) >> 12) + 27; 172 | 173 | offsetSP[0] = (eeData[58] & 0x03FF); 174 | if (offsetSP[0] > 511) { 175 | offsetSP[0] = offsetSP[0] - 1024; 176 | } 177 | 178 | offsetSP[1] = (eeData[58] & 0xFC00) >> 10; 179 | if (offsetSP[1] > 31) { 180 | offsetSP[1] = offsetSP[1] - 64; 181 | } 182 | offsetSP[1] = offsetSP[1] + offsetSP[0]; 183 | 184 | alphaSP[0] = (eeData[57] & 0x03FF); 185 | if (alphaSP[0] > 511) { 186 | alphaSP[0] = alphaSP[0] - 1024; 187 | } 188 | alphaSP[0] = alphaSP[0] / pow(2, (double)alphaScale); 189 | 190 | alphaSP[1] = (eeData[57] & 0xFC00) >> 10; 191 | if (alphaSP[1] > 31) { 192 | alphaSP[1] = alphaSP[1] - 64; 193 | } 194 | alphaSP[1] = (1 + alphaSP[1] / 128) * alphaSP[0]; 195 | 196 | cpKta = (eeData[59] & 0x00FF); 197 | if (cpKta > 127) { 198 | cpKta = cpKta - 256; 199 | } 200 | ktaScale1 = ((eeData[56] & 0x00F0) >> 4) + 8; 201 | this->cpKta = cpKta / pow(2, (double)ktaScale1); 202 | 203 | cpKv = (eeData[59] & 0xFF00) >> 8; 204 | if (cpKv > 127) { 205 | cpKv = cpKv - 256; 206 | } 207 | kvScale = (eeData[56] & 0x0F00) >> 8; 208 | this->cpKv = cpKv / pow(2, (double)kvScale); 209 | 210 | this->cpAlpha[0] = alphaSP[0]; 211 | this->cpAlpha[1] = alphaSP[1]; 212 | this->cpOffset[0] = offsetSP[0]; 213 | this->cpOffset[1] = offsetSP[1]; 214 | } 215 | 216 | void setAlphaParameters(const uint16_t *eeData) { 217 | int accRow[24]; 218 | int accColumn[32]; 219 | int p = 0; 220 | int alphaRef; 221 | uint8_t alphaScale; 222 | uint8_t accRowScale; 223 | uint8_t accColumnScale; 224 | uint8_t accRemScale; 225 | float alphaTemp[768]; 226 | float temp; 227 | 228 | accRemScale = eeData[32] & 0x000F; 229 | accColumnScale = (eeData[32] & 0x00F0) >> 4; 230 | accRowScale = (eeData[32] & 0x0F00) >> 8; 231 | alphaScale = ((eeData[32] & 0xF000) >> 12) + 30; 232 | alphaRef = eeData[33]; 233 | 234 | for (int i = 0; i < 6; i++) { 235 | p = i * 4; 236 | accRow[p + 0] = (eeData[34 + i] & 0x000F); 237 | accRow[p + 1] = (eeData[34 + i] & 0x00F0) >> 4; 238 | accRow[p + 2] = (eeData[34 + i] & 0x0F00) >> 8; 239 | accRow[p + 3] = (eeData[34 + i] & 0xF000) >> 12; 240 | } 241 | 242 | for (int i = 0; i < 24; i++) { 243 | if (accRow[i] > 7) { 244 | accRow[i] = accRow[i] - 16; 245 | } 246 | } 247 | 248 | for (int i = 0; i < 8; i++) { 249 | p = i * 4; 250 | accColumn[p + 0] = (eeData[40 + i] & 0x000F); 251 | accColumn[p + 1] = (eeData[40 + i] & 0x00F0) >> 4; 252 | accColumn[p + 2] = (eeData[40 + i] & 0x0F00) >> 8; 253 | accColumn[p + 3] = (eeData[40 + i] & 0xF000) >> 12; 254 | } 255 | 256 | for (int i = 0; i < 32; i++) { 257 | if (accColumn[i] > 7) { 258 | accColumn[i] = accColumn[i] - 16; 259 | } 260 | } 261 | 262 | for (int i = 0; i < 24; i++) { 263 | for (int j = 0; j < 32; j++) { 264 | p = 32 * i + j; 265 | alphaTemp[p] = (eeData[64 + p] & 0x03F0) >> 4; 266 | if (alphaTemp[p] > 31) { 267 | alphaTemp[p] = alphaTemp[p] - 64; 268 | } 269 | alphaTemp[p] = alphaTemp[p] * (1 << accRemScale); 270 | alphaTemp[p] = 271 | (alphaRef + (accRow[i] << accRowScale) + 272 | (accColumn[j] << accColumnScale) + alphaTemp[p]); 273 | alphaTemp[p] = alphaTemp[p] / pow(2, (double)alphaScale); 274 | alphaTemp[p] = 275 | alphaTemp[p] - 276 | this->tgc * (this->cpAlpha[0] + this->cpAlpha[1]) / 2; 277 | alphaTemp[p] = SCALEALPHA / alphaTemp[p]; 278 | } 279 | } 280 | 281 | temp = alphaTemp[0]; 282 | for (int i = 1; i < 768; i++) { 283 | if (alphaTemp[i] > temp) { 284 | temp = alphaTemp[i]; 285 | } 286 | } 287 | 288 | alphaScale = 0; 289 | while (temp < 32768) { 290 | temp = temp * 2; 291 | alphaScale = alphaScale + 1; 292 | } 293 | 294 | for (int i = 0; i < 768; i++) { 295 | temp = alphaTemp[i] * pow(2, (double)alphaScale); 296 | this->alpha[i] = (temp + 0.5); 297 | } 298 | this->alphaScale = alphaScale; 299 | } 300 | 301 | void setOffsetParameters(const uint16_t *eeData) { 302 | int occRow[24]; 303 | int occColumn[32]; 304 | int p = 0; 305 | int16_t offsetRef; 306 | uint8_t occRowScale; 307 | uint8_t occColumnScale; 308 | uint8_t occRemScale; 309 | 310 | occRemScale = (eeData[16] & 0x000F); 311 | occColumnScale = (eeData[16] & 0x00F0) >> 4; 312 | occRowScale = (eeData[16] & 0x0F00) >> 8; 313 | offsetRef = eeData[17]; 314 | if (offsetRef > 32767) { 315 | offsetRef = offsetRef - 65536; 316 | } 317 | 318 | for (int i = 0; i < 6; i++) { 319 | p = i * 4; 320 | occRow[p + 0] = (eeData[18 + i] & 0x000F); 321 | occRow[p + 1] = (eeData[18 + i] & 0x00F0) >> 4; 322 | occRow[p + 2] = (eeData[18 + i] & 0x0F00) >> 8; 323 | occRow[p + 3] = (eeData[18 + i] & 0xF000) >> 12; 324 | } 325 | 326 | for (int i = 0; i < 24; i++) { 327 | if (occRow[i] > 7) { 328 | occRow[i] = occRow[i] - 16; 329 | } 330 | } 331 | 332 | for (int i = 0; i < 8; i++) { 333 | p = i * 4; 334 | occColumn[p + 0] = (eeData[24 + i] & 0x000F); 335 | occColumn[p + 1] = (eeData[24 + i] & 0x00F0) >> 4; 336 | occColumn[p + 2] = (eeData[24 + i] & 0x0F00) >> 8; 337 | occColumn[p + 3] = (eeData[24 + i] & 0xF000) >> 12; 338 | } 339 | 340 | for (int i = 0; i < 32; i++) { 341 | if (occColumn[i] > 7) { 342 | occColumn[i] = occColumn[i] - 16; 343 | } 344 | } 345 | 346 | for (int i = 0; i < 24; i++) { 347 | for (int j = 0; j < 32; j++) { 348 | p = 32 * i + j; 349 | this->offset[p] = (eeData[64 + p] & 0xFC00) >> 10; 350 | if (this->offset[p] > 31) { 351 | this->offset[p] = this->offset[p] - 64; 352 | } 353 | this->offset[p] = this->offset[p] * (1 << occRemScale); 354 | this->offset[p] = 355 | (offsetRef + (occRow[i] << occRowScale) + 356 | (occColumn[j] << occColumnScale) + this->offset[p]); 357 | } 358 | } 359 | } 360 | 361 | void setKtaPixelParameters(const uint16_t *eeData) { 362 | int p = 0; 363 | int8_t KtaRC[4]; 364 | int8_t KtaRoCo; 365 | int8_t KtaRoCe; 366 | int8_t KtaReCo; 367 | int8_t KtaReCe; 368 | uint8_t ktaScale1; 369 | uint8_t ktaScale2; 370 | uint8_t split; 371 | float ktaTemp[768]; 372 | float temp; 373 | 374 | KtaRoCo = (eeData[54] & 0xFF00) >> 8; 375 | if (KtaRoCo > 127) { 376 | KtaRoCo = KtaRoCo - 256; 377 | } 378 | KtaRC[0] = KtaRoCo; 379 | 380 | KtaReCo = (eeData[54] & 0x00FF); 381 | if (KtaReCo > 127) { 382 | KtaReCo = KtaReCo - 256; 383 | } 384 | KtaRC[2] = KtaReCo; 385 | 386 | KtaRoCe = (eeData[55] & 0xFF00) >> 8; 387 | if (KtaRoCe > 127) { 388 | KtaRoCe = KtaRoCe - 256; 389 | } 390 | KtaRC[1] = KtaRoCe; 391 | 392 | KtaReCe = (eeData[55] & 0x00FF); 393 | if (KtaReCe > 127) { 394 | KtaReCe = KtaReCe - 256; 395 | } 396 | KtaRC[3] = KtaReCe; 397 | 398 | ktaScale1 = ((eeData[56] & 0x00F0) >> 4) + 8; 399 | ktaScale2 = (eeData[56] & 0x000F); 400 | 401 | for (int i = 0; i < 24; i++) { 402 | for (int j = 0; j < 32; j++) { 403 | p = 32 * i + j; 404 | split = 2 * (p / 32 - (p / 64) * 2) + p % 2; 405 | ktaTemp[p] = (eeData[64 + p] & 0x000E) >> 1; 406 | if (ktaTemp[p] > 3) { 407 | ktaTemp[p] = ktaTemp[p] - 8; 408 | } 409 | ktaTemp[p] = ktaTemp[p] * (1 << ktaScale2); 410 | ktaTemp[p] = KtaRC[split] + ktaTemp[p]; 411 | ktaTemp[p] = ktaTemp[p] / pow(2, (double)ktaScale1); 412 | // ktaTemp[p] = ktaTemp[p] * this->offset[p]; 413 | } 414 | } 415 | 416 | temp = fabs(ktaTemp[0]); 417 | for (int i = 1; i < 768; i++) { 418 | if (fabs(ktaTemp[i]) > temp) { 419 | temp = fabs(ktaTemp[i]); 420 | } 421 | } 422 | 423 | ktaScale1 = 0; 424 | while (temp < 64) { 425 | temp = temp * 2; 426 | ktaScale1 = ktaScale1 + 1; 427 | } 428 | 429 | for (int i = 0; i < 768; i++) { 430 | temp = ktaTemp[i] * pow(2, (double)ktaScale1); 431 | if (temp < 0) { 432 | this->kta[i] = (temp - 0.5); 433 | } else { 434 | this->kta[i] = (temp + 0.5); 435 | } 436 | } 437 | 438 | this->ktaScale = ktaScale1; 439 | } 440 | 441 | void setKvPixelParameters(const uint16_t *eeData) { 442 | int p = 0; 443 | int8_t KvT[4]; 444 | int8_t KvRoCo; 445 | int8_t KvRoCe; 446 | int8_t KvReCo; 447 | int8_t KvReCe; 448 | uint8_t kvScale; 449 | uint8_t split; 450 | float kvTemp[768]; 451 | float temp; 452 | 453 | KvRoCo = (eeData[52] & 0xF000) >> 12; 454 | if (KvRoCo > 7) { 455 | KvRoCo = KvRoCo - 16; 456 | } 457 | KvT[0] = KvRoCo; 458 | 459 | KvReCo = (eeData[52] & 0x0F00) >> 8; 460 | if (KvReCo > 7) { 461 | KvReCo = KvReCo - 16; 462 | } 463 | KvT[2] = KvReCo; 464 | 465 | KvRoCe = (eeData[52] & 0x00F0) >> 4; 466 | if (KvRoCe > 7) { 467 | KvRoCe = KvRoCe - 16; 468 | } 469 | KvT[1] = KvRoCe; 470 | 471 | KvReCe = (eeData[52] & 0x000F); 472 | if (KvReCe > 7) { 473 | KvReCe = KvReCe - 16; 474 | } 475 | KvT[3] = KvReCe; 476 | 477 | kvScale = (eeData[56] & 0x0F00) >> 8; 478 | 479 | for (int i = 0; i < 24; i++) { 480 | for (int j = 0; j < 32; j++) { 481 | p = 32 * i + j; 482 | split = 2 * (p / 32 - (p / 64) * 2) + p % 2; 483 | kvTemp[p] = KvT[split]; 484 | kvTemp[p] = kvTemp[p] / pow(2, (double)kvScale); 485 | // kvTemp[p] = kvTemp[p] * this->offset[p]; 486 | } 487 | } 488 | 489 | temp = fabs(kvTemp[0]); 490 | for (int i = 1; i < 768; i++) { 491 | if (fabs(kvTemp[i]) > temp) { 492 | temp = fabs(kvTemp[i]); 493 | } 494 | } 495 | 496 | kvScale = 0; 497 | while (temp < 64) { 498 | temp = temp * 2; 499 | kvScale = kvScale + 1; 500 | } 501 | 502 | for (int i = 0; i < 768; i++) { 503 | temp = kvTemp[i] * pow(2, (double)kvScale); 504 | if (temp < 0) { 505 | this->kv[i] = (temp - 0.5); 506 | } else { 507 | this->kv[i] = (temp + 0.5); 508 | } 509 | } 510 | 511 | this->kvScale = kvScale; 512 | } 513 | 514 | void setCILCParameters(const uint16_t *eeData) { 515 | float ilChessC[3]; 516 | uint8_t calibrationModeEE; 517 | 518 | calibrationModeEE = (eeData[10] & 0x0800) >> 4; 519 | calibrationModeEE = calibrationModeEE ^ 0x80; 520 | 521 | ilChessC[0] = (eeData[53] & 0x003F); 522 | if (ilChessC[0] > 31) { 523 | ilChessC[0] = ilChessC[0] - 64; 524 | } 525 | ilChessC[0] = ilChessC[0] / 16.0f; 526 | 527 | ilChessC[1] = (eeData[53] & 0x07C0) >> 6; 528 | if (ilChessC[1] > 15) { 529 | ilChessC[1] = ilChessC[1] - 32; 530 | } 531 | ilChessC[1] = ilChessC[1] / 2.0f; 532 | 533 | ilChessC[2] = (eeData[53] & 0xF800) >> 11; 534 | if (ilChessC[2] > 15) { 535 | ilChessC[2] = ilChessC[2] - 32; 536 | } 537 | ilChessC[2] = ilChessC[2] / 8.0f; 538 | 539 | this->calibrationModeEE = calibrationModeEE; 540 | this->ilChessC[0] = ilChessC[0]; 541 | this->ilChessC[1] = ilChessC[1]; 542 | this->ilChessC[2] = ilChessC[2]; 543 | } 544 | 545 | int setDeviatingPixels(const uint16_t *eeData) { 546 | uint16_t pixCnt = 0; 547 | uint16_t brokenPixCnt = 0; 548 | uint16_t outlierPixCnt = 0; 549 | int warn = 0; 550 | int i; 551 | 552 | for (pixCnt = 0; pixCnt < 5; pixCnt++) { 553 | this->brokenPixels[pixCnt] = 0xFFFF; 554 | this->outlierPixels[pixCnt] = 0xFFFF; 555 | } 556 | 557 | pixCnt = 0; 558 | while (pixCnt < 768 && brokenPixCnt < 5 && outlierPixCnt < 5) { 559 | if (eeData[pixCnt + 64] == 0) { 560 | this->brokenPixels[brokenPixCnt] = pixCnt; 561 | brokenPixCnt = brokenPixCnt + 1; 562 | } else if ((eeData[pixCnt + 64] & 0x0001) != 0) { 563 | this->outlierPixels[outlierPixCnt] = pixCnt; 564 | outlierPixCnt = outlierPixCnt + 1; 565 | } 566 | 567 | pixCnt = pixCnt + 1; 568 | } 569 | #if defined(DEBUG_BROKENPIXEL) 570 | this->brokenPixels[brokenPixCnt] = DEBUG_BROKENPIXEL; 571 | brokenPixCnt = brokenPixCnt + 1; 572 | #endif 573 | 574 | if (brokenPixCnt > 4) { 575 | warn = -3; 576 | } else if (outlierPixCnt > 4) { 577 | warn = -4; 578 | } else if ((brokenPixCnt + outlierPixCnt) > 4) { 579 | warn = -5; 580 | } else { 581 | for (pixCnt = 0; pixCnt < brokenPixCnt; pixCnt++) { 582 | for (i = pixCnt + 1; i < brokenPixCnt; i++) { 583 | warn = CheckAdjacentPixels(this->brokenPixels[pixCnt], 584 | this->brokenPixels[i]); 585 | if (warn != 0) { 586 | return warn; 587 | } 588 | } 589 | } 590 | 591 | for (pixCnt = 0; pixCnt < outlierPixCnt; pixCnt++) { 592 | for (i = pixCnt + 1; i < outlierPixCnt; i++) { 593 | warn = CheckAdjacentPixels(this->outlierPixels[pixCnt], 594 | this->outlierPixels[i]); 595 | if (warn != 0) { 596 | return warn; 597 | } 598 | } 599 | } 600 | 601 | for (pixCnt = 0; pixCnt < brokenPixCnt; pixCnt++) { 602 | for (i = 0; i < outlierPixCnt; i++) { 603 | warn = CheckAdjacentPixels(this->brokenPixels[pixCnt], 604 | this->outlierPixels[i]); 605 | if (warn != 0) { 606 | return warn; 607 | } 608 | } 609 | } 610 | } 611 | return warn; 612 | } 613 | 614 | void setParam(const uint16_t *eeData) { 615 | setVDDParameters(eeData); 616 | setPTATParameters(eeData); 617 | setGainParameters(eeData); 618 | setTgcParameters(eeData); 619 | setResolutionParameters(eeData); 620 | setKsTaParameters(eeData); 621 | setKsToParameters(eeData); 622 | setCPParameters(eeData); 623 | setAlphaParameters(eeData); 624 | setOffsetParameters(eeData); 625 | setKtaPixelParameters(eeData); 626 | setKvPixelParameters(eeData); 627 | setCILCParameters(eeData); 628 | setDeviatingPixels(eeData); 629 | } 630 | 631 | float MLX90640_GetVdd(const uint16_t *frameData) const { 632 | float vdd; 633 | float resolutionCorrection; 634 | 635 | int resolutionRAM; 636 | 637 | vdd = frameData[810]; 638 | if (vdd > 32767) { 639 | vdd = vdd - 65536; 640 | } 641 | resolutionRAM = (frameData[832] & 0x0C00) >> 10; 642 | resolutionCorrection = 643 | pow(2, (double)this->resolutionEE) / pow(2, (double)resolutionRAM); 644 | vdd = (resolutionCorrection * vdd - this->vdd25) / this->kVdd + 3.3; 645 | 646 | return vdd; 647 | } 648 | 649 | float MLX90640_GetTa(const uint16_t *frameData) const { 650 | float ptat; 651 | float ptatArt; 652 | float vdd; 653 | float ta; 654 | 655 | vdd = MLX90640_GetVdd(frameData); 656 | 657 | ptat = frameData[800]; 658 | if (ptat > 32767) { 659 | ptat = ptat - 65536; 660 | } 661 | 662 | ptatArt = frameData[768]; 663 | if (ptatArt > 32767) { 664 | ptatArt = ptatArt - 65536; 665 | } 666 | ptatArt = 667 | (ptat / (ptat * this->alphaPTAT + ptatArt)) * pow(2, (double)18); 668 | 669 | ta = (ptatArt / (1 + this->KvPTAT * (vdd - 3.3)) - this->vPTAT25); 670 | ta = ta / this->KtPTAT + 25; 671 | 672 | return ta; 673 | } 674 | 675 | void MLX90640_CalculateTo( 676 | const uint16_t *frameData, float emissivity, float tr, 677 | m5::MLX90640_Class::temp_data_t *result, 678 | const m5::MLX90640_Class::temp_data_t *prev_result, 679 | uint32_t filter_level) { 680 | result->min_info.temp = UINT16_MAX; 681 | result->max_info.temp = 0; 682 | int32_t total_temp = 0; 683 | // uint32_t diff_temp = 0; 684 | 685 | float irDataCP[2]; 686 | float alphaCorrR[4]; 687 | int8_t range; 688 | bool subPage = frameData[833]; 689 | 690 | float vdd_minus_33 = MLX90640_GetVdd(frameData) - 3.3; 691 | float ta = MLX90640_GetTa(frameData); 692 | 693 | float ta4 = (ta + 273.15f); 694 | ta4 *= ta4; 695 | ta4 *= ta4; 696 | 697 | float tr4 = (tr + 273.15f); 698 | tr4 *= tr4; 699 | tr4 *= tr4; 700 | 701 | float taTr = tr4 - (tr4 - ta4) / emissivity; 702 | 703 | float ktaScale = pow(2, (double)this->ktaScale); 704 | float kvScale = pow(2, (double)this->kvScale); 705 | float alphaScale = pow(2, (double)this->alphaScale); 706 | 707 | alphaCorrR[0] = 1 / (1 + this->ksTo[0] * 40); 708 | alphaCorrR[1] = 1; 709 | alphaCorrR[2] = (1 + this->ksTo[1] * this->ct[2]); 710 | alphaCorrR[3] = 711 | alphaCorrR[2] * (1 + this->ksTo[2] * (this->ct[3] - this->ct[2])); 712 | 713 | //------------------------- Gain calculation 714 | //----------------------------------- 715 | float gain = frameData[778]; 716 | if (gain > 32767) { 717 | gain -= 65536; 718 | } 719 | gain = this->gainEE / gain; 720 | 721 | //------------------------- To calculation 722 | //------------------------------------- 723 | uint8_t mode = (frameData[832] & 0x1000) >> 5; 724 | 725 | irDataCP[0] = frameData[776]; 726 | irDataCP[1] = frameData[808]; 727 | for (int i = 0; i < 2; i++) { 728 | if (irDataCP[i] > 32767) { 729 | irDataCP[i] -= 65536; 730 | } 731 | irDataCP[i] *= gain; 732 | } 733 | irDataCP[0] -= this->cpOffset[0] * (1 + this->cpKta * (ta - 25)) * 734 | (1 + this->cpKv * vdd_minus_33); 735 | if (mode == this->calibrationModeEE) { 736 | irDataCP[1] -= this->cpOffset[1] * (1 + this->cpKta * (ta - 25)) * 737 | (1 + this->cpKv * vdd_minus_33); 738 | } else { 739 | irDataCP[1] -= (this->cpOffset[1] + this->ilChessC[0]) * 740 | (1 + this->cpKta * (ta - 25)) * 741 | (1 + this->cpKv * vdd_minus_33); 742 | } 743 | 744 | float ksTo127315 = 1 - this->ksTo[1] * 273.15f; 745 | 746 | for (int i = 0; i < 384; ++i) { 747 | int ilPattern = (i >> 4) & 1; 748 | int pixelNumber = (i << 1) + ((ilPattern ^ subPage) & 1); 749 | // int chessPattern = ilPattern ^ (pixelNumber & 1); 750 | /* 751 | if (mode == 0) 752 | { 753 | if (ilPattern != subPage) continue; 754 | } 755 | else 756 | { 757 | if (chessPattern != subPage) continue; 758 | } 759 | //*/ 760 | // if (pixelNumber < 768) 761 | bool isBroken = false; 762 | 763 | for (int idx = 0; !isBroken && idx < 5 && brokenPixels[idx] < 768; 764 | ++idx) { 765 | isBroken = (pixelNumber == brokenPixels[idx]); 766 | } 767 | for (int idx = 0; !isBroken && idx < 5 && outlierPixels[idx] < 768; 768 | ++idx) { 769 | isBroken = (pixelNumber == outlierPixels[idx]); 770 | } 771 | 772 | if (!isBroken) { 773 | int conversionPattern = 774 | (((pixelNumber + 2) >> 2) - ((pixelNumber + 3) >> 2) + 775 | ((pixelNumber + 1) >> 2) - (pixelNumber >> 2)) * 776 | (1 - 2 * ilPattern); 777 | 778 | int tmp = frameData[pixelNumber]; 779 | #if defined(DEBUG_BROKENPIXEL) 780 | if (pixelNumber == DEBUG_BROKENPIXEL) { 781 | tmp = 0x7FFF; 782 | } 783 | #endif 784 | if (tmp > 32767) { 785 | tmp -= 65536; 786 | } 787 | float irData = gain * tmp; 788 | 789 | float kta = this->kta[pixelNumber] / ktaScale; 790 | float kv = this->kv[pixelNumber] / kvScale; 791 | irData = irData - this->offset[pixelNumber] * 792 | (1 + kta * (ta - 25)) * 793 | (1 + kv * vdd_minus_33); 794 | 795 | if (mode != this->calibrationModeEE) { 796 | irData = irData + this->ilChessC[2] * (2 * ilPattern - 1) - 797 | this->ilChessC[1] * conversionPattern; 798 | } 799 | 800 | irData = irData - this->tgc * irDataCP[subPage]; 801 | irData = irData / emissivity; 802 | 803 | float alphaCompensated = 804 | SCALEALPHA * alphaScale / this->alpha[pixelNumber]; 805 | alphaCompensated = 806 | alphaCompensated * (1 + this->KsTa * (ta - 25)); 807 | 808 | float Sx = alphaCompensated * alphaCompensated * 809 | alphaCompensated * 810 | (irData + alphaCompensated * taTr); 811 | // Sx = fast_sqrt2(Sx) * this->ksTo[1]; 812 | // float To = fast_sqrt2(irData / (alphaCompensated * 813 | // (ksTo127315) + Sx) + taTr) - 273.15f; 814 | Sx = sqrtf(sqrtf(Sx)) * this->ksTo[1]; 815 | float To = sqrtf(sqrtf( 816 | irData / (alphaCompensated * (ksTo127315) + Sx) + 817 | taTr)) - 818 | 273.15f; 819 | 820 | if (To < this->ct[1]) { 821 | range = 0; 822 | } else if (To < this->ct[2]) { 823 | range = 1; 824 | } else if (To < this->ct[3]) { 825 | range = 2; 826 | } else { 827 | range = 3; 828 | } 829 | auto temp = (int32_t)roundf( 830 | (sqrtf( 831 | sqrtf(irData / (alphaCompensated * alphaCorrR[range] * 832 | (1 + this->ksTo[range] * 833 | (To - this->ct[range]))) + 834 | taTr)) + 835 | ((float)m5::MLX90640_Class::DATA_OFFSET - 273.15f)) * 836 | m5::MLX90640_Class::DATA_RATIO_VALUE); 837 | if (temp < 0) { 838 | temp = 0; 839 | } else if (temp > UINT16_MAX) { 840 | temp = UINT16_MAX; 841 | } 842 | if (filter_level) { /// フィルタ処理 843 | /// (前回の温度と比較して一定以上の差がないと反応させない) 844 | int x = (pixelNumber & 31) - 15; 845 | if (x < 0) { 846 | x = ~x; 847 | } 848 | int y = (pixelNumber >> 5) - 13; 849 | if (y < 0) { 850 | y = ~y; 851 | } 852 | // 外周ピクセルほどノイズが多いため、ピクセル位置に応じてテーブルから補正係数を掛ける; 853 | int noise_filter = 854 | (filter_level * (96 + noise_tbl[x + (y * 17)])) >> 8; 855 | 856 | int diff = temp - prev_result->data[i]; 857 | if (abs(diff) > noise_filter) { 858 | temp += (diff < 0) ? noise_filter : -noise_filter; 859 | } else { 860 | temp = prev_result->data[i]; 861 | // if (diff) temp += (diff < 0) ? -1 : 1; 862 | } 863 | } 864 | result->data[i] = temp; 865 | } else { // 破損ピクセルの場合、前回の結果を用いて隣接ピクセル(最大4点)の平均を用いて更新する; 866 | int pn = pixelNumber - 32; 867 | size_t x = pn & 31; 868 | size_t y = pn >> 5; 869 | uint32_t sum = 0; 870 | int count = 0; 871 | if (x > 0) { 872 | ++count; 873 | sum += prev_result->data[(pn - 1) >> 1]; 874 | } 875 | if (x < 31) { 876 | ++count; 877 | sum += prev_result->data[(pn + 1) >> 1]; 878 | } 879 | if (y > 0) { 880 | ++count; 881 | sum += prev_result->data[(pn - 32) >> 1]; 882 | } 883 | if (y < 23) { 884 | ++count; 885 | sum += prev_result->data[(pn + 32) >> 1]; 886 | } 887 | result->data[i] = sum / count; 888 | } 889 | } 890 | } 891 | 892 | void MLX90640_CalculateTo(const uint16_t *frameData, float emissivity, 893 | float tr, uint16_t *result) { 894 | float irDataCP[2]; 895 | float alphaCorrR[4]; 896 | int8_t range; 897 | bool subPage = frameData[833]; 898 | 899 | float vdd_minus_33 = MLX90640_GetVdd(frameData) - 3.3; 900 | float ta = MLX90640_GetTa(frameData); 901 | 902 | float ta4 = (ta + 273.15f); 903 | ta4 *= ta4; 904 | ta4 *= ta4; 905 | 906 | float tr4 = (tr + 273.15f); 907 | tr4 *= tr4; 908 | tr4 *= tr4; 909 | 910 | float taTr = tr4 - (tr4 - ta4) / emissivity; 911 | 912 | float ktaScale = pow(2, (double)this->ktaScale); 913 | float kvScale = pow(2, (double)this->kvScale); 914 | float alphaScale = pow(2, (double)this->alphaScale); 915 | 916 | alphaCorrR[0] = 1 / (1 + this->ksTo[0] * 40); 917 | alphaCorrR[1] = 1; 918 | alphaCorrR[2] = (1 + this->ksTo[1] * this->ct[2]); 919 | alphaCorrR[3] = 920 | alphaCorrR[2] * (1 + this->ksTo[2] * (this->ct[3] - this->ct[2])); 921 | 922 | //------------------------- Gain calculation 923 | //----------------------------------- 924 | float gain = frameData[778]; 925 | if (gain > 32767) { 926 | gain -= 65536; 927 | } 928 | gain = this->gainEE / gain; 929 | 930 | //------------------------- To calculation 931 | //------------------------------------- 932 | uint8_t mode = (frameData[832] & 0x1000) >> 5; 933 | 934 | irDataCP[0] = frameData[776]; 935 | irDataCP[1] = frameData[808]; 936 | for (int i = 0; i < 2; i++) { 937 | if (irDataCP[i] > 32767) { 938 | irDataCP[i] -= 65536; 939 | } 940 | irDataCP[i] *= gain; 941 | } 942 | irDataCP[0] -= this->cpOffset[0] * (1 + this->cpKta * (ta - 25)) * 943 | (1 + this->cpKv * vdd_minus_33); 944 | if (mode == this->calibrationModeEE) { 945 | irDataCP[1] -= this->cpOffset[1] * (1 + this->cpKta * (ta - 25)) * 946 | (1 + this->cpKv * vdd_minus_33); 947 | } else { 948 | irDataCP[1] -= (this->cpOffset[1] + this->ilChessC[0]) * 949 | (1 + this->cpKta * (ta - 25)) * 950 | (1 + this->cpKv * vdd_minus_33); 951 | } 952 | 953 | float ksTo127315 = 1 - this->ksTo[1] * 273.15f; 954 | 955 | for (int i = 0; i < 384; ++i) { 956 | int ilPattern = (i >> 4) & 1; 957 | int pixelNumber = (i << 1) + ((ilPattern ^ subPage) & 1); 958 | 959 | { 960 | int conversionPattern = 961 | (((pixelNumber + 2) >> 2) - ((pixelNumber + 3) >> 2) + 962 | ((pixelNumber + 1) >> 2) - (pixelNumber >> 2)) * 963 | (1 - 2 * ilPattern); 964 | 965 | int tmp = frameData[pixelNumber]; 966 | #if defined(DEBUG_BROKENPIXEL) 967 | if (pixelNumber == DEBUG_BROKENPIXEL) { 968 | tmp = 0x7FFF; 969 | } 970 | #endif 971 | if (tmp > 32767) { 972 | tmp -= 65536; 973 | } 974 | float irData = gain * tmp; 975 | 976 | float kta = this->kta[pixelNumber] / ktaScale; 977 | float kv = this->kv[pixelNumber] / kvScale; 978 | irData = irData - this->offset[pixelNumber] * 979 | (1 + kta * (ta - 25)) * 980 | (1 + kv * vdd_minus_33); 981 | 982 | if (mode != this->calibrationModeEE) { 983 | irData = irData + this->ilChessC[2] * (2 * ilPattern - 1) - 984 | this->ilChessC[1] * conversionPattern; 985 | } 986 | 987 | irData = irData - this->tgc * irDataCP[subPage]; 988 | irData = irData / emissivity; 989 | 990 | float alphaCompensated = 991 | SCALEALPHA * alphaScale / this->alpha[pixelNumber]; 992 | alphaCompensated = 993 | alphaCompensated * (1 + this->KsTa * (ta - 25)); 994 | 995 | float Sx = alphaCompensated * alphaCompensated * 996 | alphaCompensated * 997 | (irData + alphaCompensated * taTr); 998 | // Sx = fast_sqrt2(Sx) * this->ksTo[1]; 999 | // float To = fast_sqrt2(irData / (alphaCompensated * 1000 | // (ksTo127315) + Sx) + taTr) - 273.15f; 1001 | Sx = sqrtf(sqrtf(Sx)) * this->ksTo[1]; 1002 | float To = sqrtf(sqrtf( 1003 | irData / (alphaCompensated * (ksTo127315) + Sx) + 1004 | taTr)) - 1005 | 273.15f; 1006 | 1007 | if (To < this->ct[1]) { 1008 | range = 0; 1009 | } else if (To < this->ct[2]) { 1010 | range = 1; 1011 | } else if (To < this->ct[3]) { 1012 | range = 2; 1013 | } else { 1014 | range = 3; 1015 | } 1016 | auto temp = (int32_t)roundf( 1017 | (sqrtf( 1018 | sqrtf(irData / (alphaCompensated * alphaCorrR[range] * 1019 | (1 + this->ksTo[range] * 1020 | (To - this->ct[range]))) + 1021 | taTr)) + 1022 | ((float)m5::MLX90640_Class::DATA_OFFSET - 273.15f)) * 1023 | m5::MLX90640_Class::DATA_RATIO_VALUE); 1024 | if (temp < 0) { 1025 | temp = 0; 1026 | } else if (temp > UINT16_MAX) { 1027 | temp = UINT16_MAX; 1028 | } 1029 | result[i] = temp; 1030 | } 1031 | } 1032 | 1033 | // 破損ピクセル箇所の補間処理 1034 | // (隣接ピクセル(最大4点)の平均を用いて更新する) 1035 | for (auto bp : {brokenPixels, outlierPixels}) { 1036 | for (int idx = 0; bp[idx] < 768 && idx < 5; ++idx) { 1037 | auto pixelNumber = bp[idx]; 1038 | int i = pixelNumber >> 1; 1039 | int ilPattern = (i >> 4) & 1; 1040 | if (pixelNumber == ((i << 1) + ((ilPattern ^ subPage) & 1))) { 1041 | size_t x = pixelNumber & 31; 1042 | size_t y = pixelNumber >> 5; 1043 | uint32_t sum = 0; 1044 | int count = 0; 1045 | if (x > 1) { 1046 | ++count; 1047 | sum += result[i - 1]; 1048 | } 1049 | if (x < 30) { 1050 | ++count; 1051 | sum += result[i + 1]; 1052 | } 1053 | if (y > 0) { 1054 | ++count; 1055 | sum += result[i - 16]; 1056 | } 1057 | if (y < 23) { 1058 | ++count; 1059 | sum += result[i + 16]; 1060 | } 1061 | result[i] = sum / count; 1062 | } 1063 | } 1064 | } 1065 | } 1066 | }; 1067 | 1068 | static MLX90640_params_t MLX90640_params; 1069 | 1070 | bool MLX90640_Class::readReg(uint16_t reg, uint16_t *data, size_t len) { 1071 | return _i2c->start(_i2c_addr, false, 400000) && _i2c->writeWords(®, 1) && 1072 | _i2c->restart(_i2c_addr, true, 400000) && 1073 | _i2c->readWords(data, len, true, _i2c_freq) && _i2c->stop(); 1074 | } 1075 | 1076 | bool MLX90640_Class::writeReg(uint16_t reg, const uint16_t *data, size_t len) { 1077 | return _i2c->start(_i2c_addr, false, 400000) && _i2c->writeWords(®, 1) && 1078 | _i2c->writeWords(data, len) && _i2c->stop(); 1079 | } 1080 | 1081 | bool MLX90640_Class::writeReg(uint16_t reg, uint16_t value) { 1082 | return writeReg(reg, &value, 1); 1083 | } 1084 | 1085 | bool MLX90640_Class::init(I2C_Master *i2c) { 1086 | _i2c = i2c; 1087 | 1088 | uint16_t data[1024]; 1089 | if (readReg(0x2400, data, 832)) { 1090 | MLX90640_params.setParam(data); 1091 | return true; 1092 | } 1093 | return false; 1094 | } 1095 | 1096 | void MLX90640_Class::setRate(refresh_rate_t rate) { 1097 | int r = rate & 7; 1098 | _refresh_rate = (refresh_rate_t)r; 1099 | // If the refresh rate is 32 Hz or higher, the communication speed is 1100 | // increased because I2C 400 kHz cannot meet the refresh cycle. 1101 | uint32_t freq = 9375u << r; 1102 | if (freq < 100000) { 1103 | freq = 100000; 1104 | } 1105 | _i2c_freq = freq; 1106 | 1107 | uint16_t tmp; 1108 | 1109 | while (!readReg(0x800D, &tmp, 1)) { 1110 | vTaskDelay(1); 1111 | } 1112 | 1113 | uint16_t value = (tmp & 0xFC7F) | (_refresh_rate << 7); 1114 | writeReg(0x800D, &value, 1); 1115 | writeReg(0x8000, 0x0030); 1116 | } 1117 | 1118 | bool MLX90640_Class::readFrameData(uint16_t *data) { 1119 | if (!readReg(0x8000, data, 1) || !((data[0] & 0x08))) return false; 1120 | data[833] = data[0] & 1; // subPage 1121 | 1122 | if (readReg(0x0400, data, 832) && readReg(0x800D, &data[832], 1)) { 1123 | // データ破損対策:830番が異常値になっていないかチェックする; 1124 | return (data[830] < 0xFF) && writeReg(0x8000, 0x0030); 1125 | } 1126 | return false; 1127 | } 1128 | 1129 | void MLX90640_Class::calcTempData(const uint16_t *framedata, 1130 | temp_data_t *tempdata, float emissivity) { 1131 | float Ta = MLX90640_params.MLX90640_GetTa(framedata); 1132 | 1133 | // Reflected temperature based on the sensor ambient temperature 1134 | float tr = Ta - TA_SHIFT; 1135 | 1136 | tempdata->subpage = framedata[833]; 1137 | MLX90640_params.MLX90640_CalculateTo(framedata, emissivity, tr, 1138 | tempdata->data); 1139 | } 1140 | 1141 | void MLX90640_Class::calcTempData(const uint16_t *framedata, 1142 | temp_data_t *tempdata, 1143 | const temp_data_t *prev_tempdata, 1144 | uint32_t filter_level, uint8_t monitor_width, 1145 | uint8_t monitor_height) { 1146 | float emissivity = 0.95; 1147 | 1148 | float Ta = MLX90640_params.MLX90640_GetTa(framedata); 1149 | 1150 | // Reflected temperature based on the sensor ambient temperature 1151 | float tr = Ta - TA_SHIFT; 1152 | 1153 | bool subpage = framedata[833]; 1154 | tempdata->subpage = subpage; 1155 | MLX90640_params.MLX90640_CalculateTo(framedata, emissivity, tr, tempdata, 1156 | prev_tempdata, filter_level); 1157 | 1158 | uint16_t data[384]; 1159 | uint16_t min_temp = UINT16_MAX; 1160 | uint16_t max_temp = 0; 1161 | uint16_t min_idx = 0; 1162 | uint16_t max_idx = 0; 1163 | 1164 | uint32_t total_temp = 0; 1165 | uint_fast8_t mx = 16 - monitor_width; 1166 | uint_fast8_t my = 12 - monitor_height; 1167 | uint_fast8_t ye = my + monitor_height * 2; 1168 | uint_fast16_t dst_idx = 0; 1169 | auto src = tempdata->data; 1170 | for (uint_fast8_t y = my; y < ye; ++y) { 1171 | auto idx = 1172 | y * (PIXEL_COLS >> 1) + ((mx + ((mx + y + subpage) & 1)) >> 1); 1173 | for (uint_fast8_t i = 0; i < monitor_width; ++i, ++idx, ++dst_idx) { 1174 | auto temp = src[idx]; 1175 | data[dst_idx] = temp; 1176 | total_temp += temp; 1177 | if (min_temp > temp) { 1178 | min_temp = temp; 1179 | min_idx = idx; 1180 | } 1181 | if (max_temp < temp) { 1182 | max_temp = temp; 1183 | max_idx = idx; 1184 | } 1185 | } 1186 | } 1187 | tempdata->avg_temp = total_temp / dst_idx; 1188 | 1189 | size_t n = dst_idx / 2; 1190 | std::nth_element(data, &data[n], &data[dst_idx]); 1191 | tempdata->med_temp = data[n]; 1192 | 1193 | tempdata->min_info.temp = min_temp; 1194 | tempdata->max_info.temp = max_temp; 1195 | { 1196 | int y = max_idx >> 4; 1197 | tempdata->max_info.y = y; 1198 | tempdata->max_info.x = ((max_idx & 15) << 1) + ((y ^ subpage) & 1); 1199 | 1200 | y = min_idx >> 4; 1201 | tempdata->min_info.y = y; 1202 | tempdata->min_info.x = ((min_idx & 15) << 1) + ((y ^ subpage) & 1); 1203 | } 1204 | } 1205 | } // namespace m5 -------------------------------------------------------------------------------- /src/webserverTask.cpp: -------------------------------------------------------------------------------- 1 | //! Copyright (c) M5Stack. All rights reserved. 2 | //! Licensed under the MIT license. 3 | //! See LICENSE file in the project root for full license information. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "screenshot_streamer.hpp" 11 | 12 | #include "common_header.h" 13 | 14 | static constexpr const char HTTP_200_html[] = 15 | "HTTP/1.1 200 OK\nContent-Type: text/html; " 16 | "charset=UTF-8\nX-Content-Type-Options: nosniff\nConnection: " 17 | "keep-alive\nCache-Control: no-cache\n\n"; 18 | static constexpr const char HTTP_200_json[] = 19 | "HTTP/1.1 200 OK\nContent-Type: application/json; " 20 | "charset=UTF-8\nX-Content-Type-Options: nosniff\nConnection: " 21 | "keep-alive\nCache-Control: no-cache\n\n"; 22 | static constexpr const char HTML_footer[] = 23 | "
Copyright ©2022 " 24 | "M5Stack
\n\n\n"; 25 | 26 | static constexpr const char HTML_style[] = 27 | ""; 64 | 65 | struct connection_t { 66 | uint32_t connect_millis = 0; 67 | WiFiClient client; 68 | std::string line_buf; 69 | std::string request_path; 70 | std::string request_post; 71 | std::string request_get; 72 | bool keep_connection = false; 73 | bool connected = false; 74 | bool is_post = false; 75 | char boundary[8] = {0}; 76 | 77 | void clear_request(void) { 78 | is_post = false; 79 | line_buf.clear(); 80 | request_path.clear(); 81 | request_post.clear(); 82 | request_get.clear(); 83 | } 84 | void stop(void) { 85 | client.stop(); 86 | keep_connection = false; 87 | connected = false; 88 | clear_request(); 89 | } 90 | }; 91 | 92 | static size_t decode_uri(char* dest, const char* src, size_t bufsiz) { 93 | int status = 0; 94 | size_t current = 0; 95 | const char* p = src; 96 | if (bufsiz == 0) return 0; 97 | bufsiz--; 98 | 99 | while (*p != 0 && current < bufsiz) { 100 | if (p[0] == '%' && p[1] != 0 && p[2] != 0) { 101 | sscanf(&p[1], "%2X", &dest[current]); 102 | p += 3; 103 | } else if (p[0] == '+') { 104 | dest[current] = ' '; 105 | p += 1; 106 | } else { 107 | dest[current] = *p++; 108 | } 109 | ++current; 110 | } 111 | dest[current] = 0; 112 | return current; 113 | } 114 | 115 | static void redirect_header(WiFiClient* client, const char* path) { 116 | client->printf( 117 | "HTTP/1.1 302 Found\nContent-Type: text/html\nContent-Length: " 118 | "0\nLocation: %s\n\n", 119 | path); 120 | } 121 | 122 | // return: true=keep connect 123 | static bool response_404(draw_param_t* draw_param, connection_t* conn) { 124 | auto client = &conn->client; 125 | if (WiFi.getMode() & WIFI_AP) { 126 | redirect_header(client, "/wifi"); 127 | } else { 128 | client->print( 129 | "HTTP/1.1 404 Not Found\nContent-type: text/html\n\n" 130 | "404 Page not found.
\n\n"); 131 | } 132 | return false; 133 | } 134 | 135 | static bool response_main(draw_param_t* draw_param, connection_t* conn) { 136 | auto client = &conn->client; 137 | 138 | static constexpr const char html_1[] = 139 | "T-Lite\n\n" 164 | "
\n" 165 | "

T-Lite

\n" 166 | "
"; 167 | 168 | std::string strbuf; 169 | strbuf.reserve(8192); 170 | char cbuf[64]; 171 | 172 | strbuf = html_1; 173 | strbuf += 174 | "\n\n"; 199 | 200 | strbuf += 201 | "\n
    \n" 203 | "
  • Alarm Mode:
  • \n"; 211 | 212 | strbuf += "
  • Temperature: "; 213 | strbuf.append(cbuf, 214 | snprintf(cbuf, sizeof(cbuf), "%3.1f", 215 | convertRawToCelsius(draw_param->alarm_temperature))); 216 | strbuf += 217 | "
    \n
  • "; 221 | strbuf += 222 | "
  • Reference:
  • \n
\n"; 231 | strbuf += 232 | "\n
    \n" 234 | "
  • Refresh Rate:
  • \n"; 243 | 244 | strbuf += 245 | "
  • Noise Filter:
  • \n"; 254 | 255 | strbuf += 256 | "
  • Monitor Area:
  • \n"; 265 | 266 | strbuf += "
  • Emissivity: "; 267 | strbuf.append(cbuf, snprintf(cbuf, sizeof(cbuf), "%d", 268 | draw_param->sens_emissivity.get())); 269 | // strbuf += draw_param->sens_emissivity.getText(); 270 | strbuf += 271 | "
    \n\n
\n"; 275 | 276 | strbuf += 277 | "\n
    \n" 279 | "
  • Auto Range:
  • \n"; 288 | strbuf += "
  • Upper Temperature: "; 289 | strbuf.append(cbuf, 290 | snprintf(cbuf, sizeof(cbuf), "%3.1f", 291 | convertRawToCelsius(draw_param->range_temp_upper))); 292 | strbuf += 293 | "
    \n
  • "; 297 | strbuf += "
  • Lower Temperature: "; 298 | strbuf.append(cbuf, 299 | snprintf(cbuf, sizeof(cbuf), "%3.1f", 300 | convertRawToCelsius(draw_param->range_temp_lower))); 301 | strbuf += 302 | "
    \n\n
\n"; 307 | 308 | strbuf += 309 | "\n
    \n" 311 | "
  • CPU Speed:
  • \n"; 320 | 321 | strbuf += 322 | "
  • Sound Volume:
  • \n"; 331 | 332 | strbuf += 333 | "
  • LCD Brightness:
  • \n"; 342 | 343 | strbuf += 344 | "
  • Language:
  • \n"; 353 | 354 | strbuf += "
  • LAN Stream Quality: "; 355 | strbuf += draw_param->net_jpg_quality.getText(); 356 | strbuf += 357 | "
    \n\n"; 361 | 362 | strbuf += 363 | "
  • Pointer:
  • \n"; 371 | 372 | strbuf += 373 | "
  • Color:
  • \n"; 381 | 382 | strbuf += 383 | "
  • Layout:
  • \n
\n"; 391 | strbuf += HTML_footer; 392 | 393 | // client->print(HTTP_200_html); 394 | client->print( 395 | "HTTP/1.1 200 OK\nContent-Type: text/html; " 396 | "charset=UTF-8\nX-Content-Type-Options: nosniff\nConnection: " 397 | "keep-alive\nCache-Control: no-cache\n"); 398 | client->printf("Content-Length: %d\n\n", strbuf.size()); 399 | client->write(strbuf.c_str(), strbuf.size()); 400 | client->print("\n"); 401 | return true; 402 | } 403 | 404 | static bool response_param(draw_param_t* draw_param, connection_t* conn) { 405 | auto client = &conn->client; 406 | int pos = conn->request_get.find('='); 407 | if (pos >= 0) { 408 | auto key = conn->request_get.substr(0, pos); 409 | ++pos; 410 | auto val = conn->request_get.substr(pos); 411 | 412 | if (key == "alarm_temperature") { 413 | draw_param->alarm_temperature = 414 | convertCelsiusToRaw(atof(val.c_str())); 415 | } else if (key == "range_temp_upper") { 416 | draw_param->range_temp_upper = 417 | convertCelsiusToRaw(atof(val.c_str())); 418 | } else if (key == "range_temp_lower") { 419 | draw_param->range_temp_lower = 420 | convertCelsiusToRaw(atof(val.c_str())); 421 | } else if (key == "cloud_token") { 422 | draw_param->cloud_token = val.c_str(); 423 | } else { 424 | int v = atoi(val.c_str()); 425 | if (key == "alarm_mode") { 426 | draw_param->alarm_mode.set(v); 427 | } else if (key == "alarm_reference") { 428 | draw_param->alarm_reference.set(v); 429 | } 430 | // else if (key == "alarm_behavior" ) { 431 | // draw_param->alarm_behavior .set(v); } 432 | else if (key == "sens_refreshrate") { 433 | draw_param->sens_refreshrate.set(v); 434 | } else if (key == "sens_noisefilter") { 435 | draw_param->sens_noisefilter.set(v); 436 | } else if (key == "sens_monitorarea") { 437 | draw_param->sens_monitorarea.set(v); 438 | } else if (key == "sens_emissivity") { 439 | draw_param->sens_emissivity.set(v); 440 | } else if (key == "range_autoswitch") { 441 | draw_param->range_autoswitch.set(v); 442 | } else if (key == "net_jpg_quality") { 443 | draw_param->net_jpg_quality.set(v); 444 | } else if (key == "misc_cpuspeed") { 445 | draw_param->misc_cpuspeed.set(v); 446 | } else if (key == "misc_volume") { 447 | draw_param->misc_volume.set(v); 448 | } else if (key == "misc_brightness") { 449 | draw_param->misc_brightness.set(v); 450 | } else if (key == "misc_language") { 451 | draw_param->misc_language.set(v); 452 | } else if (key == "misc_pointer") { 453 | draw_param->misc_pointer.set(v); 454 | } else if (key == "misc_layout") { 455 | draw_param->misc_layout.set(v); 456 | draw_param->in_config_mode = false; 457 | } else if (key == "misc_color") { 458 | draw_param->misc_color.set(v); 459 | } 460 | // else if (key == "cloud_upload" ) { draw_param->cloud_upload 461 | // .set(v); } 462 | else if (key == "cloud_interval") { 463 | draw_param->cloud_interval.set(v); 464 | } 465 | } 466 | // draw_param->saveNvs(); 467 | } 468 | 469 | std::string strbuf; 470 | char cbuf[64]; 471 | strbuf.append( 472 | cbuf, 473 | snprintf(cbuf, sizeof(cbuf), "{\n \"alarm_temperature\": \"%3.1f\"", 474 | convertRawToCelsius(draw_param->alarm_temperature))); 475 | strbuf.append(cbuf, 476 | snprintf(cbuf, sizeof(cbuf), ",\n \"alarm_mode\": \"%d\"", 477 | draw_param->alarm_mode.get())); 478 | strbuf.append( 479 | cbuf, snprintf(cbuf, sizeof(cbuf), ",\n \"alarm_reference\": \"%d\"", 480 | draw_param->alarm_reference.get())); 481 | strbuf.append( 482 | cbuf, snprintf(cbuf, sizeof(cbuf), ",\n \"sens_refreshrate\": \"%d\"", 483 | draw_param->sens_refreshrate.get())); 484 | strbuf.append( 485 | cbuf, snprintf(cbuf, sizeof(cbuf), ",\n \"sens_noisefilter\": \"%d\"", 486 | draw_param->sens_noisefilter.get())); 487 | strbuf.append( 488 | cbuf, snprintf(cbuf, sizeof(cbuf), ",\n \"sens_monitorarea\": \"%d\"", 489 | draw_param->sens_monitorarea.get())); 490 | strbuf.append( 491 | cbuf, snprintf(cbuf, sizeof(cbuf), ",\n \"sens_emissivity\": \"%d\"", 492 | draw_param->sens_emissivity.get())); 493 | strbuf.append( 494 | cbuf, snprintf(cbuf, sizeof(cbuf), ",\n \"range_autoswitch\": \"%d\"", 495 | draw_param->range_autoswitch.get())); 496 | strbuf.append( 497 | cbuf, 498 | snprintf(cbuf, sizeof(cbuf), ",\n \"range_temp_upper\": \"%3.1f\"", 499 | convertRawToCelsius(draw_param->range_temp_upper))); 500 | strbuf.append( 501 | cbuf, 502 | snprintf(cbuf, sizeof(cbuf), ",\n \"range_temp_lower\": \"%3.1f\"", 503 | convertRawToCelsius(draw_param->range_temp_lower))); 504 | strbuf.append( 505 | cbuf, snprintf(cbuf, sizeof(cbuf), ",\n \"net_jpg_quality\": \"%d\"", 506 | draw_param->net_jpg_quality.get())); 507 | strbuf.append(cbuf, 508 | snprintf(cbuf, sizeof(cbuf), ",\n \"misc_cpuspeed\": \"%d\"", 509 | draw_param->misc_cpuspeed.get())); 510 | strbuf.append(cbuf, 511 | snprintf(cbuf, sizeof(cbuf), ",\n \"misc_volume\": \"%d\"", 512 | draw_param->misc_volume.get())); 513 | strbuf.append( 514 | cbuf, snprintf(cbuf, sizeof(cbuf), ",\n \"misc_brightness\": \"%d\"", 515 | draw_param->misc_brightness.get())); 516 | strbuf.append(cbuf, 517 | snprintf(cbuf, sizeof(cbuf), ",\n \"misc_language\": \"%d\"", 518 | draw_param->misc_language.get())); 519 | strbuf.append(cbuf, 520 | snprintf(cbuf, sizeof(cbuf), ",\n \"misc_pointer\": \"%d\"", 521 | draw_param->misc_pointer.get())); 522 | strbuf.append(cbuf, 523 | snprintf(cbuf, sizeof(cbuf), ",\n \"misc_layout\": \"%d\"", 524 | draw_param->misc_layout.get())); 525 | strbuf.append(cbuf, 526 | snprintf(cbuf, sizeof(cbuf), ",\n \"misc_color\": \"%d\"", 527 | draw_param->misc_color.get())); 528 | strbuf.append(cbuf, 529 | snprintf(cbuf, sizeof(cbuf), ",\n \"cloud_interval\": \"%d\"", 530 | draw_param->cloud_interval.get())); 531 | strbuf.append(cbuf, 532 | snprintf(cbuf, sizeof(cbuf), ",\n \"cloud_token\": \"%s\"", 533 | draw_param->cloud_token.c_str())); 534 | strbuf += "\n}\n\n"; 535 | 536 | client->print( 537 | "HTTP/1.1 200 OK\nContent-Type: application/json; " 538 | "charset=UTF-8\nX-Content-Type-Options: nosniff\nConnection: " 539 | "keep-alive\nCache-Control: no-cache\n"); 540 | client->printf("Content-Length: %d\n\n", strbuf.size()); 541 | client->write(strbuf.c_str(), strbuf.size()); 542 | client->print("\n"); 543 | return true; 544 | } 545 | 546 | static bool response_json(draw_param_t* draw_param, connection_t* conn) { 547 | auto client = &conn->client; 548 | /* 549 | auto t = time(nullptr); 550 | auto gmt = gmtime(&t); 551 | client->printf("{\"center\":\"%6.1f\",", 552 | convertRawToCelsius(draw_param->frame->temp[framedata_t::center])); 553 | client->printf("\"highest\":\"%6.1f\",", 554 | convertRawToCelsius(draw_param->frame->temp[framedata_t::highest])); 555 | client->printf("\"average\":\"%6.1f\",", 556 | convertRawToCelsius(draw_param->frame->temp[framedata_t::average])); 557 | client->printf("\"lowest\":\"%6.1f\",", 558 | convertRawToCelsius(draw_param->frame->temp[framedata_t::lowest])); 559 | client->printf("\"date\":\"%s, %d %s %04d %02d:%02d:%02d GMT\"}\n", 560 | wday_tbl[gmt->tm_wday], gmt->tm_mday, mon_tbl[gmt->tm_mon], gmt->tm_year 561 | + 1900, gmt->tm_hour, gmt->tm_min, gmt->tm_sec); 562 | */ 563 | std::string strbuf; 564 | { 565 | auto frame = *draw_param->frame; 566 | strbuf = frame.getJsonData(); 567 | } 568 | 569 | // client->print(HTTP_200_json); 570 | client->print( 571 | "HTTP/1.1 200 OK\nContent-Type: application/json; " 572 | "charset=UTF-8\nX-Content-Type-Options: nosniff\nConnection: " 573 | "keep-alive\nCache-Control: no-cache\n"); 574 | client->printf("Content-Length: %d\n\n", strbuf.size()); 575 | client->write(strbuf.c_str(), strbuf.size()); 576 | client->print("\n"); 577 | return true; 578 | } 579 | 580 | static bool response_text(draw_param_t* draw_param, connection_t* conn) { 581 | auto client = &conn->client; 582 | auto t = time(nullptr); 583 | auto gmt = gmtime(&t); 584 | 585 | static constexpr const char html_1[] = 586 | "\n" 587 | "\n" 589 | "\n" 590 | "T-Lite Text Info\n\n\n"; 591 | char cbuf[128]; 592 | 593 | std::string strbuf = html_1; 594 | strbuf.append(cbuf, 595 | snprintf(cbuf, sizeof(cbuf), 596 | "", 597 | wday_tbl[gmt->tm_wday], gmt->tm_mday, 598 | mon_tbl[gmt->tm_mon], gmt->tm_year + 1900)); 599 | strbuf.append(cbuf, 600 | snprintf(cbuf, sizeof(cbuf), 601 | "", 602 | gmt->tm_hour, gmt->tm_min, gmt->tm_sec)); 603 | strbuf.append( 604 | cbuf, 605 | snprintf( 606 | cbuf, sizeof(cbuf), "\n", 607 | convertRawToCelsius(draw_param->frame->temp[framedata_t::center]))); 608 | strbuf.append(cbuf, 609 | snprintf(cbuf, sizeof(cbuf), 610 | "\n", 611 | convertRawToCelsius( 612 | draw_param->frame->temp[framedata_t::highest]))); 613 | strbuf.append(cbuf, 614 | snprintf(cbuf, sizeof(cbuf), 615 | "\n", 616 | convertRawToCelsius( 617 | draw_param->frame->temp[framedata_t::average]))); 618 | strbuf.append( 619 | cbuf, 620 | snprintf( 621 | cbuf, sizeof(cbuf), "\n", 622 | convertRawToCelsius(draw_param->frame->temp[framedata_t::lowest]))); 623 | strbuf += "
date %s, %d %s %04d
time %02d:%02d:%02d GMT
center %3.1f
highest%3.1f
average%3.1f
lowest %3.1f
\n\n"; 624 | 625 | client->print( 626 | "HTTP/1.1 200 OK\nContent-Type: text/html; " 627 | "charset=UTF-8\nX-Content-Type-Options: nosniff\nConnection: " 628 | "keep-alive\nCache-Control: no-cache\n"); 629 | client->printf("Content-Length: %d\n\n", strbuf.size() - 1); 630 | client->write(strbuf.c_str(), strbuf.size()); 631 | 632 | return true; 633 | } 634 | 635 | static bool response_stream(draw_param_t* draw_param, connection_t* conn) { 636 | auto client = &conn->client; 637 | client->print("HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\n"); 638 | client->print("Content-type: multipart/x-mixed-replace;boundary="); 639 | client->print(conn->boundary); 640 | client->print("\r\n"); 641 | screenshot_holder.requestScreenShot(client); 642 | // JPEGストリームを受け取るため戻り値をtrue (keep connection)にする 643 | return true; 644 | } 645 | 646 | static bool response_wifi(draw_param_t* draw_param, connection_t* conn) { 647 | auto client = &conn->client; 648 | // APモードでなければ wifi設定を使用できないようにする 649 | // if (draw_param->net_setup_mode.get() == draw_param->net_setup_mode_off) { 650 | if (!(WiFi.getMode() & WIFI_AP)) { 651 | redirect_header(client, "/"); 652 | return false; 653 | } 654 | //*/ 655 | if (conn->request_post.length()) { 656 | std::string ssid, password; 657 | int pos1 = 0; 658 | int pos2 = 0; 659 | bool end = false; 660 | do { 661 | pos1 = conn->request_post.find('=', pos2); 662 | if (pos1 < 0) break; 663 | auto key = conn->request_post.substr(pos2, pos1 - pos2); 664 | pos2 = conn->request_post.find('&', ++pos1); 665 | end = (pos2 < 0); 666 | if (end) pos2 = conn->request_post.length(); 667 | auto val = conn->request_post.substr(pos1, pos2++ - pos1++); 668 | // ESP_LOGE("DEBUG","key : %s val : %s", key.c_str(), val.c_str()); 669 | 670 | char buf[64]; 671 | decode_uri(buf, val.c_str(), sizeof(buf)); 672 | if (key == "s") { 673 | ssid = buf; 674 | } else if (key == "p") { 675 | password = buf; 676 | } 677 | } while (!end); 678 | redirect_header(client, "/wifi"); 679 | 680 | if (ssid.length()) { 681 | draw_param->sys_ssid = ssid; 682 | draw_param->net_tmp_ssid = ssid; 683 | draw_param->net_tmp_pwd = password; 684 | draw_param->net_running_mode = draw_param->net_running_mode_offline; 685 | delay(64); 686 | draw_param->net_running_mode = 687 | draw_param->net_running_mode_lan_cloud; 688 | /* 689 | WiFi.begin(ssid.c_str(), password.c_str()); 690 | client->setTimeout(10); 691 | int retry = 2048; 692 | do { 693 | delay(1); 694 | } while (!WiFi.isConnected() && --retry); 695 | if (retry) { 696 | static constexpr const char html_success_1[] = 697 | "T-Lite\n" 699 | "

T-Lite

\n
    " 705 | "
  • WiFi connected !
  • \n
  • "; 706 | 707 | static constexpr const char html_success_2[] = 708 | "
  • \n
"; 709 | 710 | client->print(HTTP_200_html); 711 | client->print(html_success_1); 712 | client->printf("%s", 713 | draw_param->net_url.c_str(), draw_param->net_url.c_str()); 714 | client->print(html_success_2); 715 | client->print(HTML_footer); 716 | client->stop(); 717 | 718 | delay(256); 719 | 720 | draw_param->net_setup_mode = 721 | draw_param->net_setup_mode_off; return false; 722 | } 723 | //*/ 724 | } 725 | return false; 726 | } 727 | 728 | static constexpr const char html_1[] = 729 | "\n" 731 | "\n" 733 | "T-Lite WiFi setup\n" 734 | "\n"; 744 | 745 | static constexpr const char html_2[] = 746 | "

T-Lite WiFi setup

" 747 | "
"; 748 | static constexpr const char html_3[] = "

SSID List

"; 749 | 750 | static constexpr const char html_4[] = 751 | "
" 752 | "
" 755 | "
" 757 | "
" 760 | "
"; 761 | 762 | client->print(HTTP_200_html); 763 | client->print(html_1); 764 | client->print(HTML_style); 765 | client->print(html_2); 766 | if (!draw_param->sys_ssid.empty()) { 767 | client->print("

Current SSID

"); 768 | client->printf( 769 | " %s ", 770 | draw_param->sys_ssid.c_str()); 771 | client->print("

"); 772 | } 773 | 774 | client->print(html_3); 775 | int i = 0; 776 | int count = WiFi.scanComplete(); 777 | for (int i = 0; i < count; ++i) { 778 | auto ssid = WiFi.SSID(i); 779 | client->printf( 780 | " %s ", 781 | ssid.c_str()); 782 | // WiFi.encryptionType(i) == WIFI_AUTH_OPEN ? 783 | // WiFi.RSSI(i) + "dBm"; 784 | } 785 | client->print(html_4); 786 | client->print(HTML_footer); 787 | 788 | if (count != -1) { 789 | WiFi.scanNetworks(true); 790 | } 791 | 792 | return false; 793 | } 794 | 795 | static bool response_top(draw_param_t* draw_param, connection_t* conn) { 796 | auto client = &conn->client; 797 | if ((WiFi.getMode() & WIFI_AP) && !WiFi.isConnected()) { 798 | // redirect_header(client, "/wifi"); 799 | // return false; 800 | return response_wifi(draw_param, conn); 801 | } 802 | // if (!WiFi.isConnected()) { 803 | // redirect_header(client, "/main"); 804 | // } else { 805 | // redirect_header(client, "/wifi"); 806 | // } 807 | 808 | static constexpr const char html_1[] = 809 | "\n" 810 | "\n" 812 | "T-Lite Top Menu\n"; 813 | 814 | static constexpr const char html_2[] = 815 | "

T-Lite Top menu

" 816 | "

Cloud

"; 817 | 818 | static constexpr const char html_3[] = 819 | "

LAN

"; 820 | 821 | static constexpr const char html_4[] = 822 | "Browser control\n" 823 | "Text infomation\n" 824 | "JSON data\n" 825 | "Stream Image\n" 826 | "
\n"; 827 | 828 | client->print(HTTP_200_html); 829 | client->print(html_1); 830 | client->print(HTML_style); 831 | client->print(html_2); 832 | 833 | client->printf( 834 | "Image " 835 | "(ConfirmCode:%s)", 836 | draw_param->cloud_url.c_str(), draw_param->cloud_token.c_str()); 837 | // if (WiFi.getMode() & WIFI_AP) { 838 | // client->print(html_3); 839 | // } 840 | client->print(html_3); 841 | 842 | if (WiFi.getMode() & WIFI_AP) { 843 | client->print("WiFi setting\n"); 844 | } 845 | 846 | client->print(html_4); 847 | 848 | client->print(HTML_footer); 849 | return false; 850 | } 851 | 852 | static bool response_test(draw_param_t* draw_param, connection_t* conn) { 853 | static constexpr const char head[] = 854 | "HTTP/1.1 200 OK\n" 855 | "Content-Type: text/html\n" 856 | "Content-Length: 16\n" 857 | "Connection: keep-alive\n" 858 | "Cache-Control: no-store\n" 859 | "\n"; 860 | auto client = &conn->client; 861 | 862 | client->print(head); 863 | client->print("0123456789abcdef\n\n\n"); 864 | client->flush(); 865 | return true; 866 | 867 | /*/ 868 | static constexpr const char html[] = "HTTP/1.1 200 OK\nContent-Type: 869 | text/html\nConnection:close\n\n" 870 | "" 871 | "" 882 | "

M5TLite

"; 883 | client->print(html); 884 | client->printf("

Brightness ()

" 886 | "", 889 | draw_param->perf_emissivity); client->print(""); 896 | return false; 897 | //*/ 898 | } 899 | 900 | struct response_table_t { 901 | const char* path; 902 | bool (*response_func)(draw_param_t*, connection_t*); 903 | }; 904 | 905 | static constexpr const response_table_t response_table[] = { 906 | {"/", response_top}, {"/main", response_main}, 907 | {"/json", response_json}, {"/text", response_text}, 908 | {"/wifi", response_wifi}, {"/stream", response_stream}, 909 | {"/param", response_param}, 910 | // { "/test" , response_test }, 911 | }; 912 | 913 | void webserverTask(void* arg) { 914 | auto draw_param = (draw_param_t*)arg; 915 | 916 | WiFiServer httpServer(80, 4); 917 | 918 | static constexpr const size_t connection_size = 8; 919 | connection_t connection[connection_size]; 920 | uint8_t connection_index = 0; 921 | uint32_t conn_idx = 0; 922 | bool prev_connected = false; 923 | uint8_t restart_countdown = 0; 924 | uint8_t prev_active_count = 0; 925 | uint8_t active_count = 0; 926 | uint8_t loop_counter = 0; 927 | 928 | for (;;) { 929 | // switch (screenshot_holder.processCapture()) { 930 | // screenshot_streamer_t::pr_nothing: 931 | // break; 932 | // } 933 | if (++loop_counter == 0) delay(1); 934 | if (screenshot_holder.processCapture() == 935 | screenshot_streamer_t::process_result_t::pr_nothing) { 936 | if (!active_count) { 937 | delay(1); 938 | } 939 | } 940 | // if (prev_active_mask != active_mask) { 941 | // prev_active_mask = active_mask; 942 | // ESP_EARLY_LOGD("DEBUG","httpServer active_mask = %02x", active_mask); 943 | // } 944 | bool connected = (WiFi.status() == WL_CONNECTED) || 945 | (WiFi.getMode() & wifi_mode_t::WIFI_MODE_AP); 946 | 947 | if (prev_connected != connected) { 948 | prev_connected = connected; 949 | if (connected) { 950 | httpServer.begin(); 951 | // httpServer.setTimeout(3); 952 | // httpServer.setNoDelay(true); 953 | MDNS.begin(draw_param->net_apmode_ssid); 954 | MDNS.addService("http", "tcp", 80); 955 | // ESP_EARLY_LOGD("DEBUG","httpServer begin"); 956 | } else { 957 | MDNS.end(); 958 | for (auto& conn : connection) { 959 | conn.stop(); 960 | } 961 | httpServer.end(); 962 | // ESP_EARLY_LOGD("DEBUG","httpServer end"); 963 | } 964 | } 965 | if (!connected) { 966 | delay(32); 967 | continue; 968 | } 969 | /* 970 | if (active_count == 0) { 971 | if (prev_active_count) { 972 | restart_countdown = 255; 973 | } 974 | if (restart_countdown) { 975 | if (0 == --restart_countdown) { 976 | ESP_EARLY_LOGD("DEBUG","httpServer restart"); 977 | httpServer.end(); 978 | httpServer.begin(); 979 | } 980 | } 981 | } 982 | //*/ 983 | 984 | uint32_t current_millis = millis(); 985 | 986 | if (httpServer.hasClient()) { 987 | for (int i = 0; i < connection_size; ++i) { 988 | connection_index = connection_index != connection_size - 1 989 | ? connection_index + 1 990 | : 0; 991 | if (!connection[connection_index].connected) { 992 | break; 993 | } 994 | if (!(connection[connection_index].client.connected())) { 995 | // ESP_EARLY_LOGD("DEBUG", "connected -> close"); 996 | connection[connection_index].stop(); 997 | break; 998 | } 999 | } 1000 | // ESP_EARLY_LOGD("DEBUG","connection_index: %d", connection_index); 1001 | auto& conn = connection[connection_index]; 1002 | if (!conn.connected) { 1003 | conn.connected = true; 1004 | conn.client = httpServer.available(); 1005 | snprintf(conn.boundary, sizeof(conn.boundary), "tlite"); 1006 | conn.connect_millis = current_millis; 1007 | } 1008 | } 1009 | 1010 | prev_active_count = active_count; 1011 | active_count = 0; 1012 | 1013 | for (auto& conn : connection) { 1014 | if (!conn.connected) { 1015 | continue; 1016 | } 1017 | WiFiClient* client = &(conn.client); 1018 | ++active_count; 1019 | 1020 | if (!client->available()) { 1021 | if (!client->connected()) { 1022 | // ESP_EARLY_LOGD("DEBUG", "no connected stop"); 1023 | conn.stop(); 1024 | // client->stop(); 1025 | continue; 1026 | } 1027 | if (!conn.keep_connection && 1028 | 2048 < current_millis - conn.connect_millis) { 1029 | // ESP_EARLY_LOGD("DEBUG", "keep_connection timeout stop"); 1030 | conn.stop(); 1031 | // client->stop(); 1032 | continue; 1033 | } 1034 | } else { 1035 | conn.connect_millis = current_millis; 1036 | int available_len; 1037 | while (available_len = client->available()) { 1038 | char c = client->read(); 1039 | if (c == '\r') { 1040 | continue; 1041 | } 1042 | if (c != '\n') { 1043 | conn.line_buf.append(1, c); 1044 | } else { 1045 | // ESP_EARLY_LOGD("DEBUG", "line_buf : %s", 1046 | // conn.line_buf.c_str()); 1047 | if (conn.line_buf.empty()) { 1048 | if (conn.is_post) { 1049 | char buffer[256] = { 1050 | 0, 1051 | }; 1052 | // memset(conn.post_data, 0, 1053 | // sizeof(conn.post_data)); 1054 | int len; 1055 | int retry = 256; 1056 | do { 1057 | delay(1); 1058 | } while (0 == (len = client->available()) && 1059 | --retry); 1060 | if (len > 255) len = 255; 1061 | if (len) { 1062 | client->readBytes(buffer, len); 1063 | conn.request_post = buffer; 1064 | } 1065 | // conn.post_data_len = len; 1066 | // ESP_EARLY_LOGD("DEBUG","POST_DATA : %s", 1067 | // buffer); 1068 | } 1069 | if (conn.request_path.length()) { 1070 | bool hit = false; 1071 | for (auto& res : response_table) { 1072 | // ESP_EARLY_LOGD("DEBUG","cmp:%s : %s", 1073 | // conn.request_path.c_str(), res.path); 1074 | hit = (strcmp(conn.request_path.c_str(), 1075 | res.path) == 0); 1076 | if (hit) { 1077 | conn.keep_connection = 1078 | res.response_func(draw_param, 1079 | &conn); 1080 | break; 1081 | } 1082 | } 1083 | if (!hit) { 1084 | response_404(draw_param, &conn); 1085 | } 1086 | if (conn.keep_connection == false) { 1087 | // ESP_EARLY_LOGD("DEBUG", "keep_connection 1088 | // false stop"); 1089 | conn.stop(); 1090 | // client->stop(); 1091 | } 1092 | conn.clear_request(); 1093 | // ESP_EARLY_LOGD("DEBUG", "clear_request"); 1094 | } 1095 | } else { 1096 | bool is_post = 1097 | conn.line_buf.compare(0, 6, "POST /") == 0; 1098 | if (is_post || 1099 | conn.line_buf.compare(0, 5, "GET /") == 0) { 1100 | conn.is_post = is_post; 1101 | int pos1 = conn.line_buf.find('/'); 1102 | int pos2 = conn.line_buf.find('?', pos1); 1103 | int pos3 = conn.line_buf.find(' ', pos1); 1104 | if (pos2 < 0) { 1105 | conn.request_path = 1106 | conn.line_buf.substr(pos1, pos3 - pos1) 1107 | .c_str(); 1108 | conn.request_get = ""; 1109 | } else { 1110 | conn.request_path = 1111 | conn.line_buf.substr(pos1, pos2 - pos1) 1112 | .c_str(); 1113 | ++pos2; 1114 | conn.request_get = 1115 | conn.line_buf.substr(pos2, pos3 - pos2) 1116 | .c_str(); 1117 | } 1118 | } 1119 | conn.line_buf = ""; 1120 | } 1121 | } 1122 | } 1123 | } 1124 | } 1125 | } 1126 | } 1127 | --------------------------------------------------------------------------------