diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/airmouse/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/README.md b/Applications/Official/DEV_FW/source/xMasterX/airmouse/README.md new file mode 100644 index 000000000..04e346e4b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/README.md @@ -0,0 +1,60 @@ +# Flipper Air Mouse + +## Brief + +> "You can turn anything into an air mouse if you're brave enough" + + — Piper, a.k.a. Pez + +Naturally, the quote above applies to [Flipper](https://flipperzero.one/) as well. + +## What? + +The app allows you to turn your Flipper into a USB or Bluetooth air mouse (you do need an extra module, see the Hardware section below)... + +Using it is really simple: + * Connect the Flipper via a USB cable and pick `USB`, or pick `Bluetooth` and pair it with your PC; + * Hold the Flipper in your hand with the buttons pointing towards the screen; + * Wave your Flipper like you don't care to move the cursor; + * Up button for Left mouse click; + * Down button for Right mouse click; + * Center button for Middle mouse click; + * Left and Right buttons for scrolling; + * Use calibration menu option if you notice significant drift (place your Flipper onto a level surface, make sure it doesn't move, run this option, wait 2 seconds, done). + +See early prototype [in action](https://www.youtube.com/watch?v=DdxAmmsYfMA). + +## Hardware + +The custom module is using Bosch BMI160 accelerometer/gyroscope chip connected via I2C. + +Take a look into the [schematic](https://github.com/ginkage/FlippAirMouse/tree/main/schematic) folder for Gerber, BOM and CPL files, so you can order directly from JLCPCB. + +Original idea: + +![What I thought it would look like](https://github.com/ginkage/FlippAirMouse/blob/main/schematic/schematic.png) + +Expectation: + +![What EDA though it would look like](https://github.com/ginkage/FlippAirMouse/blob/main/schematic/render.png) + +Reality: + +![What it looks like](https://github.com/ginkage/FlippAirMouse/blob/main/schematic/flipper.jpg) + +## Software + +The code is based on the original Bosch [driver](https://github.com/BoschSensortec/BMI160_driver/) and an orientation tracking implementation from the Google [Cardboard](https://github.com/googlevr/cardboard/tree/master/sdk/sensors) project + +If you're familiar with Flipper applications, start in the [firmware](https://github.com/flipperdevices/flipperzero-firmware) checkout folder and do the following: +``` +cd applications/plugins +git clone https://github.com/ginkage/FlippAirMouse +cd ../.. +./fbt fap_air_mouse +``` +If you're not familiar with those, just grab a `fap` file from Releases. + +## License + +TL;DR: Use the code however you want, give credit where it's due, no warranty of any kind is provided. diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/air_mouse.c b/Applications/Official/DEV_FW/source/xMasterX/airmouse/air_mouse.c new file mode 100644 index 000000000..7a90e49f1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/air_mouse.c @@ -0,0 +1,156 @@ +#include "air_mouse.h" + +#include +#include + +#include "tracking/imu/imu.h" + +#define TAG "AirMouseApp" + +enum AirMouseSubmenuIndex { + AirMouseSubmenuIndexBtMouse, + AirMouseSubmenuIndexUsbMouse, + AirMouseSubmenuIndexCalibration, +}; + +void air_mouse_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + AirMouse* app = context; + if(index == AirMouseSubmenuIndexBtMouse) { + app->view_id = AirMouseViewBtMouse; + view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewBtMouse); + } else if(index == AirMouseSubmenuIndexUsbMouse) { + app->view_id = AirMouseViewUsbMouse; + view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewUsbMouse); + } else if(index == AirMouseSubmenuIndexCalibration) { + app->view_id = AirMouseViewCalibration; + view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewCalibration); + } +} + +void air_mouse_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + AirMouse* app = context; + if(result == DialogExResultLeft) { + view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_NONE); // Exit + } else if(result == DialogExResultRight) { + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view + } else if(result == DialogExResultCenter) { + view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewSubmenu); // Menu + } +} + +uint32_t air_mouse_exit_confirm_view(void* context) { + UNUSED(context); + return AirMouseViewExitConfirm; +} + +uint32_t air_mouse_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +AirMouse* air_mouse_app_alloc() { + AirMouse* app = malloc(sizeof(AirMouse)); + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Submenu view + app->submenu = submenu_alloc(); + submenu_add_item( + app->submenu, "Bluetooth", AirMouseSubmenuIndexBtMouse, air_mouse_submenu_callback, app); + submenu_add_item( + app->submenu, "USB", AirMouseSubmenuIndexUsbMouse, air_mouse_submenu_callback, app); + submenu_add_item( + app->submenu, + "Calibration", + AirMouseSubmenuIndexCalibration, + air_mouse_submenu_callback, + app); + view_set_previous_callback(submenu_get_view(app->submenu), air_mouse_exit); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewSubmenu, submenu_get_view(app->submenu)); + + // Dialog view + app->dialog = dialog_ex_alloc(); + dialog_ex_set_result_callback(app->dialog, air_mouse_dialog_callback); + dialog_ex_set_context(app->dialog, app); + dialog_ex_set_left_button_text(app->dialog, "Exit"); + dialog_ex_set_right_button_text(app->dialog, "Stay"); + dialog_ex_set_center_button_text(app->dialog, "Menu"); + dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewExitConfirm, dialog_ex_get_view(app->dialog)); + + // Bluetooth view + app->bt_mouse = bt_mouse_alloc(app->view_dispatcher); + view_set_previous_callback(bt_mouse_get_view(app->bt_mouse), air_mouse_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewBtMouse, bt_mouse_get_view(app->bt_mouse)); + + // USB view + app->usb_mouse = usb_mouse_alloc(app->view_dispatcher); + view_set_previous_callback(usb_mouse_get_view(app->usb_mouse), air_mouse_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewUsbMouse, usb_mouse_get_view(app->usb_mouse)); + + // Calibration view + app->calibration = calibration_alloc(app->view_dispatcher); + view_set_previous_callback( + calibration_get_view(app->calibration), air_mouse_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, AirMouseViewCalibration, calibration_get_view(app->calibration)); + + app->view_id = AirMouseViewSubmenu; + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + + return app; +} + +void air_mouse_app_free(AirMouse* app) { + furi_assert(app); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewExitConfirm); + dialog_ex_free(app->dialog); + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewBtMouse); + bt_mouse_free(app->bt_mouse); + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewUsbMouse); + usb_mouse_free(app->usb_mouse); + view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewCalibration); + calibration_free(app->calibration); + view_dispatcher_free(app->view_dispatcher); + + // Close records + furi_record_close(RECORD_GUI); + app->gui = NULL; + + // Free rest + free(app); +} + +int32_t air_mouse_app(void* p) { + UNUSED(p); + + AirMouse* app = air_mouse_app_alloc(); + if(!imu_begin()) { + air_mouse_app_free(app); + return -1; + } + + DOLPHIN_DEED(DolphinDeedPluginStart); + view_dispatcher_run(app->view_dispatcher); + + imu_end(); + air_mouse_app_free(app); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/air_mouse.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/air_mouse.h new file mode 100644 index 000000000..3a1ba783e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/air_mouse.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "views/bt_mouse.h" +#include "views/usb_mouse.h" +#include "views/calibration.h" + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + DialogEx* dialog; + BtMouse* bt_mouse; + UsbMouse* usb_mouse; + Calibration* calibration; + uint32_t view_id; +} AirMouse; + +typedef enum { + AirMouseViewSubmenu, + AirMouseViewBtMouse, + AirMouseViewUsbMouse, + AirMouseViewCalibration, + AirMouseViewExitConfirm, +} AirMouseView; diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/application.fam b/Applications/Official/DEV_FW/source/xMasterX/airmouse/application.fam new file mode 100644 index 000000000..9014b14a6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/application.fam @@ -0,0 +1,9 @@ +App( + appid="Air_Mouse", + name="[BMI160] Air Mouse", + apptype=FlipperAppType.EXTERNAL, + entry_point="air_mouse_app", + stack_size=10 * 1024, + fap_category="GPIO_Extra", + fap_icon="mouse_10px.png", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/mouse_10px.png b/Applications/Official/DEV_FW/source/xMasterX/airmouse/mouse_10px.png new file mode 100644 index 000000000..94c3a7a14 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/airmouse/mouse_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/calibration_data.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/calibration_data.cc new file mode 100644 index 000000000..e62311c7a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/calibration_data.cc @@ -0,0 +1,85 @@ +#include +#include + +#define TAG "tracker" + +#include "calibration_data.h" + +#include +#include + +// Student's distribution T value for 95% (two-sided) confidence interval. +static const double Tn = 1.960; + +// Number of samples (degrees of freedom) for the corresponding T values. +static const int Nn = 200; + +void CalibrationData::reset() +{ + complete = false; + count = 0; + sum = Vector::Zero(); + sumSq = Vector::Zero(); + mean = Vector::Zero(); + median = Vector::Zero(); + sigma = Vector::Zero(); + delta = Vector::Zero(); + xData.clear(); + yData.clear(); + zData.clear(); +} + +bool CalibrationData::add(Vector& data) +{ + if (complete) { + return true; + } + + xData.push_back(data[0]); + yData.push_back(data[1]); + zData.push_back(data[2]); + + sum += data; + sumSq += data * data; + count++; + + if (count >= Nn) { + calcDelta(); + complete = true; + } + + return complete; +} + +static inline double medianOf(std::vector& list) +{ + std::sort(list.begin(), list.end()); + int count = list.size(); + int middle = count / 2; + return (count % 2 == 1) ? list[middle] : (list[middle - 1] + list[middle]) / 2.0l; +} + +void CalibrationData::calcDelta() +{ + median.Set(medianOf(xData), medianOf(yData), medianOf(zData)); + + mean = sum / count; + Vector m2 = mean * mean; + Vector d = sumSq / count - m2; + Vector s2 = (d * count) / (count - 1); + sigma = Vector(std::sqrt(d[0]), std::sqrt(d[1]), std::sqrt(d[2])); + Vector s = Vector(std::sqrt(s2[0]), std::sqrt(s2[1]), std::sqrt(s2[2])); + delta = s * Tn / std::sqrt((double)count); + Vector low = mean - delta; + Vector high = mean + delta; + + FURI_LOG_I(TAG, + "M[x] = { %f ... %f } // median = %f // avg = %f // delta = %f // sigma = %f", + low[0], high[0], median[0], mean[0], delta[0], sigma[0]); + FURI_LOG_I(TAG, + "M[y] = { %f ... %f } // median = %f // avg = %f // delta = %f // sigma = %f", + low[1], high[1], median[1], mean[1], delta[1], sigma[1]); + FURI_LOG_I(TAG, + "M[z] = { %f ... %f } // median = %f // avg = %f // delta = %f // sigma = %f", + low[2], high[2], median[2], mean[2], delta[2], sigma[2]); +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/calibration_data.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/calibration_data.h new file mode 100644 index 000000000..d47dab08d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/calibration_data.h @@ -0,0 +1,117 @@ +#pragma once + +#include +#include +#include + +#include "util/vector.h" + +#define CALIBRATION_DATA_VER (1) +#define CALIBRATION_DATA_FILE_NAME ".calibration.data" +#define CALIBRATION_DATA_PATH INT_PATH(CALIBRATION_DATA_FILE_NAME) +#define CALIBRATION_DATA_MAGIC (0x23) + +#define CALIBRATION_DATA_SAVE(x) \ + saved_struct_save( \ + CALIBRATION_DATA_PATH, \ + (x), \ + sizeof(CalibrationMedian), \ + CALIBRATION_DATA_MAGIC, \ + CALIBRATION_DATA_VER) + +#define CALIBRATION_DATA_LOAD(x) \ + saved_struct_load( \ + CALIBRATION_DATA_PATH, \ + (x), \ + sizeof(CalibrationMedian), \ + CALIBRATION_DATA_MAGIC, \ + CALIBRATION_DATA_VER) + +typedef struct { + double x; + double y; + double z; +} CalibrationMedian; + +typedef cardboard::Vector3 Vector; + +/** + * Helper class to gather some stats and store the calibration data. Right now it calculates a lot + * more stats than actually needed. Some of them are used for logging the sensors quality (and + * filing bugs), other may be required in the future, e.g. for bias. + */ +class CalibrationData { +public: + /** + * Check if the sensors were calibrated before. + * + * @return {@code true} if calibration data is available, or {@code false} otherwise. + */ + bool isComplete() { + return complete; + } + + /** Prepare to collect new calibration data. */ + void reset(); + + /** + * Retrieve the median gyroscope readings. + * + * @return Three-axis median vector. + */ + Vector getMedian() { + return median; + } + + /** + * Retrieve the mean gyroscope readings. + * + * @return Three-axis mean vector. + */ + Vector getMean() { + return mean; + } + + /** + * Retrieve the standard deviation of gyroscope readings. + * + * @return Three-axis standard deviation vector. + */ + Vector getSigma() { + return sigma; + } + + /** + * Retrieve the confidence interval size of gyroscope readings. + * + * @return Three-axis confidence interval size vector. + */ + Vector getDelta() { + return delta; + } + + /** + * Add a new gyroscope reading to the stats. + * + * @param data gyroscope values vector. + * @return {@code true} if we now have enough data for calibration, or {@code false} otherwise. + */ + bool add(Vector& data); + +private: + // Calculates the confidence interval (mean +- delta) and some other related values, like + // standard deviation, etc. See https://en.wikipedia.org/wiki/Student%27s_t-distribution + void calcDelta(); + + int count; + bool complete; + Vector sum; + Vector sumSq; + Vector mean; + Vector median; + Vector sigma; + Vector delta; + std::vector xData; + std::vector yData; + std::vector zData; +}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/bmi160.c b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/bmi160.c new file mode 100644 index 000000000..968dddd4d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/bmi160.c @@ -0,0 +1,5988 @@ +/** +* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved. +* +* BSD-3-Clause +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* @file bmi160.c +* @date 2021-10-05 +* @version v3.9.2 +* +*/ + +#include "bmi160.h" + +/* Below look up table follows the enum bmi160_int_types. + * Hence any change should match to the enum bmi160_int_types + */ +const uint8_t int_mask_lookup_table[13] = { + BMI160_INT1_SLOPE_MASK, + BMI160_INT1_SLOPE_MASK, + BMI160_INT2_LOW_STEP_DETECT_MASK, + BMI160_INT1_DOUBLE_TAP_MASK, + BMI160_INT1_SINGLE_TAP_MASK, + BMI160_INT1_ORIENT_MASK, + BMI160_INT1_FLAT_MASK, + BMI160_INT1_HIGH_G_MASK, + BMI160_INT1_LOW_G_MASK, + BMI160_INT1_NO_MOTION_MASK, + BMI160_INT2_DATA_READY_MASK, + BMI160_INT2_FIFO_FULL_MASK, + BMI160_INT2_FIFO_WM_MASK}; + +/*********************************************************************/ +/* Static function declarations */ + +/*! + * @brief This API configures the pins to fire the + * interrupt signal when it occurs + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_intr_pin_config(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the any-motion interrupt of the sensor. + * This interrupt occurs when accel values exceeds preset threshold + * for a certain period of time. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_any_motion_int(struct bmi160_int_settg* int_config, struct bmi160_dev* dev); + +/*! + * @brief This API sets tap interrupts.Interrupt is fired when + * tap movements happen. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_accel_tap_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the data ready interrupt for both accel and gyro. + * This interrupt occurs when new accel and gyro data come. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_accel_gyro_data_ready_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_dev* dev); + +/*! + * @brief This API sets the significant motion interrupt of the sensor.This + * interrupt occurs when there is change in user location. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_sig_motion_int(struct bmi160_int_settg* int_config, struct bmi160_dev* dev); + +/*! + * @brief This API sets the no motion/slow motion interrupt of the sensor. + * Slow motion is similar to any motion interrupt.No motion interrupt + * occurs when slope bet. two accel values falls below preset threshold + * for preset duration. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_no_motion_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the step detection interrupt.This interrupt + * occurs when the single step causes accel values to go above + * preset threshold. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_step_detect_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the orientation interrupt of the sensor.This + * interrupt occurs when there is orientation change in the sensor + * with respect to gravitational field vector g. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_orientation_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the flat interrupt of the sensor.This interrupt + * occurs in case of flat orientation + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_flat_detect_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the low-g interrupt of the sensor.This interrupt + * occurs during free-fall. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_low_g_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the high-g interrupt of the sensor.The interrupt + * occurs if the absolute value of acceleration data of any enabled axis + * exceeds the programmed threshold and the sign of the value does not + * change for a preset duration. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t + set_accel_high_g_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the default configuration parameters of accel & gyro. + * Also maintain the previous state of configurations. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static void default_param_settg(struct bmi160_dev* dev); + +/*! + * @brief This API is used to validate the device structure pointer for + * null conditions. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t null_ptr_check(const struct bmi160_dev* dev); + +/*! + * @brief This API set the accel configuration. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_accel_conf(struct bmi160_dev* dev); + +/*! + * @brief This API gets the accel configuration. + * + * @param[out] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t get_accel_conf(struct bmi160_dev* dev); + +/*! + * @brief This API check the accel configuration. + * + * @param[in] data : Pointer to store the updated accel config. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t check_accel_config(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the accel odr. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_accel_odr(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the accel bandwidth. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_accel_bw(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the accel range. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_accel_range(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API checks the invalid settings for ODR & Bw for Accel and Gyro. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t check_invalid_settg(const struct bmi160_dev* dev); + +/*! + * @brief This API set the gyro configuration. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_gyro_conf(struct bmi160_dev* dev); + +/*! + * @brief This API get the gyro configuration. + * + * @param[out] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t get_gyro_conf(struct bmi160_dev* dev); + +/*! + * @brief This API check the gyro configuration. + * + * @param[in] data : Pointer to store the updated gyro config. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t check_gyro_config(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the gyro odr. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_gyro_odr(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the gyro bandwidth. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_gyro_bw(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API process the gyro range. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_gyro_range(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the accel power mode. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_accel_pwr(struct bmi160_dev* dev); + +/*! + * @brief This API process the undersampling setting of Accel. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t process_under_sampling(uint8_t* data, const struct bmi160_dev* dev); + +/*! + * @brief This API sets the gyro power mode. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + */ +static int8_t set_gyro_pwr(struct bmi160_dev* dev); + +/*! + * @brief This API reads accel data along with sensor time if time is requested + * by user. Kindly refer the user guide(README.md) for more info. + * + * @param[in] len : len to read no of bytes + * @param[out] accel : Structure pointer to store accel data + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + get_accel_data(uint8_t len, struct bmi160_sensor_data* accel, const struct bmi160_dev* dev); + +/*! + * @brief This API reads accel data along with sensor time if time is requested + * by user. Kindly refer the user guide(README.md) for more info. + * + * @param[in] len : len to read no of bytes + * @param[out] gyro : Structure pointer to store accel data + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + get_gyro_data(uint8_t len, struct bmi160_sensor_data* gyro, const struct bmi160_dev* dev); + +/*! + * @brief This API reads accel and gyro data along with sensor time + * if time is requested by user. + * Kindly refer the user guide(README.md) for more info. + * + * @param[in] len : len to read no of bytes + * @param[out] accel : Structure pointer to store accel data + * @param[out] gyro : Structure pointer to store accel data + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t get_accel_gyro_data( + uint8_t len, + struct bmi160_sensor_data* accel, + struct bmi160_sensor_data* gyro, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the any-motion interrupt for accel. + * + * @param[in] any_motion_int_cfg : Structure instance of + * bmi160_acc_any_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_accel_any_motion_int( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + struct bmi160_dev* dev); + +/*! + * @brief This API disable the sig-motion interrupt. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t disable_sig_motion_int(const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for any-motion interrupt. + * + * @param[in] any_motion_int_cfg : Structure instance of + * bmi160_acc_any_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_any_motion_src( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the duration and threshold of + * any-motion interrupt. + * + * @param[in] any_motion_int_cfg : Structure instance of + * bmi160_acc_any_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_any_dur_threshold( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure necessary setting of any-motion interrupt. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] any_motion_int_cfg : Structure instance of + * bmi160_acc_any_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_any_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enable the data ready interrupt. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_data_ready_int(const struct bmi160_dev* dev); + +/*! + * @brief This API enables the no motion/slow motion interrupt. + * + * @param[in] no_mot_int_cfg : Structure instance of + * bmi160_acc_no_motion_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_no_motion_int( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the interrupt PIN setting for + * no motion/slow motion interrupt. + * + * @param[in] int_config : structure instance of bmi160_int_settg. + * @param[in] no_mot_int_cfg : Structure instance of + * bmi160_acc_no_motion_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_no_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of interrupt for no motion. + * + * @param[in] no_mot_int_cfg : Structure instance of + * bmi160_acc_no_motion_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_no_motion_data_src( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the duration and threshold of + * no motion/slow motion interrupt along with selection of no/slow motion. + * + * @param[in] no_mot_int_cfg : Structure instance of + * bmi160_acc_no_motion_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_no_motion_dur_thr( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the sig-motion motion interrupt. + * + * @param[in] sig_mot_int_cfg : Structure instance of + * bmi160_acc_sig_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_sig_motion_int( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + struct bmi160_dev* dev); + +/*! + * @brief This API configure the interrupt PIN setting for + * significant motion interrupt. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] sig_mot_int_cfg : Structure instance of + * bmi160_acc_sig_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_sig_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for sig motion interrupt. + * + * @param[in] sig_mot_int_cfg : Structure instance of + * bmi160_acc_sig_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_sig_motion_data_src( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the threshold, skip and proof time of + * sig motion interrupt. + * + * @param[in] sig_mot_int_cfg : Structure instance of + * bmi160_acc_sig_mot_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_sig_dur_threshold( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the step detector interrupt. + * + * @param[in] step_detect_int_cfg : Structure instance of + * bmi160_acc_step_detect_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_step_detect_int( + const struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the step detector parameter. + * + * @param[in] step_detect_int_cfg : Structure instance of + * bmi160_acc_step_detect_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_step_detect( + const struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the single/double tap interrupt. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_tap_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the interrupt PIN setting for + * tap interrupt. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] tap_int_cfg : Structure instance of bmi160_acc_tap_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_tap_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for tap interrupt. + * + * @param[in] tap_int_cfg : Structure instance of bmi160_acc_tap_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_tap_data_src( + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the parameters of tap interrupt. + * Threshold, quite, shock, and duration. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] tap_int_cfg : Structure instance of bmi160_acc_tap_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_tap_param( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enable the external mode configuration. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_sec_if(const struct bmi160_dev* dev); + +/*! + * @brief This API configure the ODR of the auxiliary sensor. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_aux_odr(const struct bmi160_dev* dev); + +/*! + * @brief This API maps the actual burst read length set by user. + * + * @param[in] len : Pointer to store the read length. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t map_read_len(uint16_t* len, const struct bmi160_dev* dev); + +/*! + * @brief This API configure the settings of auxiliary sensor. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_aux_settg(const struct bmi160_dev* dev); + +/*! + * @brief This API extract the read data from auxiliary sensor. + * + * @param[in] map_len : burst read value. + * @param[in] reg_addr : Address of register to read. + * @param[in] aux_data : Pointer to store the read data. + * @param[in] len : length to read the data. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t extract_aux_read( + uint16_t map_len, + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the orient interrupt. + * + * @param[in] orient_int_cfg : Structure instance of bmi160_acc_orient_int_cfg. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_orient_int( + const struct bmi160_acc_orient_int_cfg* orient_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the necessary setting of orientation interrupt. + * + * @param[in] orient_int_cfg : Structure instance of bmi160_acc_orient_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_orient_int_settg( + const struct bmi160_acc_orient_int_cfg* orient_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the flat interrupt. + * + * @param[in] flat_int : Structure instance of bmi160_acc_flat_detect_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_flat_int( + const struct bmi160_acc_flat_detect_int_cfg* flat_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the necessary setting of flat interrupt. + * + * @param[in] flat_int : Structure instance of bmi160_acc_flat_detect_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_flat_int_settg( + const struct bmi160_acc_flat_detect_int_cfg* flat_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the Low-g interrupt. + * + * @param[in] low_g_int : Structure instance of bmi160_acc_low_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_low_g_int( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) for low-g interrupt. + * + * @param[in] low_g_int : Structure instance of bmi160_acc_low_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_low_g_data_src( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the necessary setting of low-g interrupt. + * + * @param[in] low_g_int : Structure instance of bmi160_acc_low_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_low_g_int_settg( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev); + +/*! + * @brief This API enables the high-g interrupt. + * + * @param[in] high_g_int_cfg : Structure instance of bmi160_acc_high_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_high_g_int( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for high-g interrupt. + * + * @param[in] high_g_int_cfg : Structure instance of bmi160_acc_high_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_high_g_data_src( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the necessary setting of high-g interrupt. + * + * @param[in] high_g_int_cfg : Structure instance of bmi160_acc_high_g_int_cfg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t config_high_g_int_settg( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev); + +/*! + * @brief This API configure the behavioural setting of interrupt pin. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + config_int_out_ctrl(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API configure the mode(input enable, latch or non-latch) of interrupt pin. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + config_int_latch(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API performs the self test for accelerometer of BMI160 + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t perform_accel_self_test(struct bmi160_dev* dev); + +/*! + * @brief This API enables to perform the accel self test by setting proper + * configurations to facilitate accel self test + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_accel_self_test(struct bmi160_dev* dev); + +/*! + * @brief This API performs accel self test with positive excitation + * + * @param[in] accel_pos : Structure pointer to store accel data + * for positive excitation + * @param[in] dev : structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t accel_self_test_positive_excitation( + struct bmi160_sensor_data* accel_pos, + const struct bmi160_dev* dev); + +/*! + * @brief This API performs accel self test with negative excitation + * + * @param[in] accel_neg : Structure pointer to store accel data + * for negative excitation + * @param[in] dev : structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t accel_self_test_negative_excitation( + struct bmi160_sensor_data* accel_neg, + const struct bmi160_dev* dev); + +/*! + * @brief This API validates the accel self test results + * + * @param[in] accel_pos : Structure pointer to store accel data + * for positive excitation + * @param[in] accel_neg : Structure pointer to store accel data + * for negative excitation + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error / +ve value -> Self test fail + */ +static int8_t validate_accel_self_test( + const struct bmi160_sensor_data* accel_pos, + const struct bmi160_sensor_data* accel_neg); + +/*! + * @brief This API performs the self test for gyroscope of BMI160 + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t perform_gyro_self_test(const struct bmi160_dev* dev); + +/*! + * @brief This API enables the self test bit to trigger self test for gyro + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t enable_gyro_self_test(const struct bmi160_dev* dev); + +/*! + * @brief This API validates the self test results of gyro + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t validate_gyro_self_test(const struct bmi160_dev* dev); + +/*! + * @brief This API sets FIFO full interrupt of the sensor.This interrupt + * occurs when the FIFO is full and the next full data sample would cause + * a FIFO overflow, which may delete the old samples. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + set_fifo_full_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This enable the FIFO full interrupt engine. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + enable_fifo_full_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API sets FIFO watermark interrupt of the sensor.The FIFO + * watermark interrupt is fired, when the FIFO fill level is above a fifo + * watermark. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + set_fifo_watermark_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This enable the FIFO watermark interrupt engine. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + enable_fifo_wtm_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to reset the FIFO related configurations + * in the fifo_frame structure. + * + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void reset_fifo_data_structure(const struct bmi160_dev* dev); + +/*! + * @brief This API is used to read number of bytes filled + * currently in FIFO buffer. + * + * @param[in] bytes_to_read : Number of bytes available in FIFO at the + * instant which is obtained from FIFO counter. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error. + * @retval Any non zero value -> Fail + * + */ +static int8_t get_fifo_byte_counter(uint16_t* bytes_to_read, struct bmi160_dev const* dev); + +/*! + * @brief This API is used to compute the number of bytes of accel FIFO data + * which is to be parsed in header-less mode + * + * @param[out] data_index : The start index for parsing data + * @param[out] data_read_length : Number of bytes to be parsed + * @param[in] acc_frame_count : Number of accelerometer frames to be read + * @param[in] dev : Structure instance of bmi160_dev. + * + */ +static void get_accel_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* acc_frame_count, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed. + * + * @param[in,out] acc : structure instance of sensor data + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] acc_idx : Index value of accelerometer data + * (x,y,z axes) frames parsed + * @param[in] frame_info : It consists of either fifo_data_enable + * parameter in header-less mode or + * frame header data in header mode + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_accel_frame( + struct bmi160_sensor_data* acc, + uint16_t* idx, + uint8_t* acc_idx, + uint8_t frame_info, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data and store it in the instance of the structure bmi160_sensor_data. + * + * @param[in,out] accel_data : structure instance of sensor data + * @param[in,out] data_start_index : Index value of number of bytes parsed + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_accel_data( + struct bmi160_sensor_data* accel_data, + uint16_t data_start_index, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data in header mode. + * + * @param[in,out] accel_data : Structure instance of sensor data + * @param[in,out] accel_length : Number of accelerometer frames + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void extract_accel_header_mode( + struct bmi160_sensor_data* accel_data, + uint8_t* accel_length, + const struct bmi160_dev* dev); + +/*! + * @brief This API computes the number of bytes of gyro FIFO data + * which is to be parsed in header-less mode + * + * @param[out] data_index : The start index for parsing data + * @param[out] data_read_length : No of bytes to be parsed from FIFO buffer + * @param[in] gyro_frame_count : Number of Gyro data frames to be read + * @param[in] dev : Structure instance of bmi160_dev. + */ +static void get_gyro_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* gyro_frame_count, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the gyroscope's data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed. + * + * @param[in,out] gyro : structure instance of sensor data + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] gyro_idx : Index value of gyro data + * (x,y,z axes) frames parsed + * @param[in] frame_info : It consists of either fifo_data_enable + * parameter in header-less mode or + * frame header data in header mode + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_gyro_frame( + struct bmi160_sensor_data* gyro, + uint16_t* idx, + uint8_t* gyro_idx, + uint8_t frame_info, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the gyro data from the + * FIFO data and store it in the instance of the structure bmi160_sensor_data. + * + * @param[in,out] gyro_data : structure instance of sensor data + * @param[in,out] data_start_index : Index value of number of bytes parsed + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_gyro_data( + struct bmi160_sensor_data* gyro_data, + uint16_t data_start_index, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the gyro data from the + * FIFO data in header mode. + * + * @param[in,out] gyro_data : Structure instance of sensor data + * @param[in,out] gyro_length : Number of gyro frames + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void extract_gyro_header_mode( + struct bmi160_sensor_data* gyro_data, + uint8_t* gyro_length, + const struct bmi160_dev* dev); + +/*! + * @brief This API computes the number of bytes of aux FIFO data + * which is to be parsed in header-less mode + * + * @param[out] data_index : The start index for parsing data + * @param[out] data_read_length : No of bytes to be parsed from FIFO buffer + * @param[in] aux_frame_count : Number of Aux data frames to be read + * @param[in] dev : Structure instance of bmi160_dev. + */ +static void get_aux_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* aux_frame_count, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the aux's data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed + * + * @param[in,out] aux_data : structure instance of sensor data + * @param[in,out] idx : Index value of number of bytes parsed + * @param[in,out] aux_index : Index value of gyro data + * (x,y,z axes) frames parsed + * @param[in] frame_info : It consists of either fifo_data_enable + * parameter in header-less mode or + * frame header data in header mode + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_aux_frame( + struct bmi160_aux_data* aux_data, + uint16_t* idx, + uint8_t* aux_index, + uint8_t frame_info, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the aux data from the + * FIFO data and store it in the instance of the structure bmi160_aux_data. + * + * @param[in,out] aux_data : structure instance of sensor data + * @param[in,out] data_start_index : Index value of number of bytes parsed + * @param[in] dev : structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_aux_data( + struct bmi160_aux_data* aux_data, + uint16_t data_start_index, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse the aux data from the + * FIFO data in header mode. + * + * @param[in,out] aux_data : Structure instance of sensor data + * @param[in,out] aux_length : Number of aux frames + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void extract_aux_header_mode( + struct bmi160_aux_data* aux_data, + uint8_t* aux_length, + const struct bmi160_dev* dev); + +/*! + * @brief This API checks the presence of non-valid frames in the read fifo data. + * + * @param[in,out] data_index : The index of the current data to + * be parsed from fifo data + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void check_frame_validity(uint16_t* data_index, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to move the data index ahead of the + * current_frame_length parameter when unnecessary FIFO data appears while + * extracting the user specified data. + * + * @param[in,out] data_index : Index of the FIFO data which + * is to be moved ahead of the + * current_frame_length + * @param[in] current_frame_length : Number of bytes in a particular frame + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void move_next_frame( + uint16_t* data_index, + uint8_t current_frame_length, + const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse and store the sensor time from the + * FIFO data in the structure instance dev. + * + * @param[in,out] data_index : Index of the FIFO data which + * has the sensor time. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_sensortime_frame(uint16_t* data_index, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to parse and store the skipped_frame_count from + * the FIFO data in the structure instance dev. + * + * @param[in,out] data_index : Index of the FIFO data which + * has the skipped frame count. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static void unpack_skipped_frame(uint16_t* data_index, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to get the FOC status from the sensor + * + * @param[in,out] foc_status : Result of FOC status. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t get_foc_status(uint8_t* foc_status, struct bmi160_dev const* dev); + +/*! + * @brief This API is used to configure the offset enable bits in the sensor + * + * @param[in,out] foc_conf : Structure instance of bmi160_foc_conf which + * has the FOC and offset configurations + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + configure_offset_enable(const struct bmi160_foc_conf* foc_conf, struct bmi160_dev const* dev); + +/*! + * @brief This API is used to trigger the FOC in the sensor + * + * @param[in,out] offset : Structure instance of bmi160_offsets which + * reads and stores the offset values after FOC + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t trigger_foc(struct bmi160_offsets* offset, struct bmi160_dev const* dev); + +/*! + * @brief This API is used to map/unmap the Dataready(Accel & Gyro), FIFO full + * and FIFO watermark interrupt + * + * @param[in] int_config : Structure instance of bmi160_int_settg which + * stores the interrupt type and interrupt channel + * configurations to map/unmap the interrupt pins + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + map_hardware_interrupt(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*! + * @brief This API is used to map/unmap the Any/Sig motion, Step det/Low-g, + * Double tap, Single tap, Orientation, Flat, High-G, Nomotion interrupt pins. + * + * @param[in] int_config : Structure instance of bmi160_int_settg which + * stores the interrupt type and interrupt channel + * configurations to map/unmap the interrupt pins + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval zero -> Success / -ve value -> Error + */ +static int8_t + map_feature_interrupt(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev); + +/*********************** User function definitions ****************************/ + +/*! + * @brief This API reads the data from the given register address + * of sensor. + */ +int8_t + bmi160_get_regs(uint8_t reg_addr, uint8_t* data, uint16_t len, const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + /* Null-pointer check */ + if((dev == NULL) || (dev->read == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else if(len == 0) { + rslt = BMI160_E_READ_WRITE_LENGTH_INVALID; + } else { + /* Configuring reg_addr for SPI Interface */ + if(dev->intf == BMI160_SPI_INTF) { + reg_addr = (reg_addr | BMI160_SPI_RD_MASK); + } + + rslt = dev->read(dev->id, reg_addr, data, len); + } + + return rslt; +} + +/*! + * @brief This API writes the given data to the register address + * of sensor. + */ +int8_t + bmi160_set_regs(uint8_t reg_addr, uint8_t* data, uint16_t len, const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint8_t count = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->write == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else if(len == 0) { + rslt = BMI160_E_READ_WRITE_LENGTH_INVALID; + } else { + /* Configuring reg_addr for SPI Interface */ + if(dev->intf == BMI160_SPI_INTF) { + reg_addr = (reg_addr & BMI160_SPI_WR_MASK); + } + + if((dev->prev_accel_cfg.power == BMI160_ACCEL_NORMAL_MODE) || + (dev->prev_gyro_cfg.power == BMI160_GYRO_NORMAL_MODE)) { + rslt = dev->write(dev->id, reg_addr, data, len); + + /* Kindly refer bmi160 data sheet section 3.2.4 */ + dev->delay_ms(1); + + } else { + /*Burst write is not allowed in + * suspend & low power mode */ + for(; count < len; count++) { + rslt = dev->write(dev->id, reg_addr, &data[count], 1); + reg_addr++; + + /* Kindly refer bmi160 data sheet section 3.2.4 */ + dev->delay_ms(1); + } + } + + if(rslt != BMI160_OK) { + rslt = BMI160_E_COM_FAIL; + } + } + + return rslt; +} + +/*! + * @brief This API is the entry point for sensor.It performs + * the selection of I2C/SPI read mechanism according to the + * selected interface and reads the chip-id of bmi160 sensor. + */ +int8_t bmi160_init(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data; + uint8_t try = 3; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + + /* Dummy read of 0x7F register to enable SPI Interface + * if SPI is used */ + if((rslt == BMI160_OK) && (dev->intf == BMI160_SPI_INTF)) { + rslt = bmi160_get_regs(BMI160_SPI_COMM_TEST_ADDR, &data, 1, dev); + } + + if(rslt == BMI160_OK) { + /* Assign chip id as zero */ + dev->chip_id = 0; + + while((try--) && (dev->chip_id != BMI160_CHIP_ID)) { + /* Read chip_id */ + rslt = bmi160_get_regs(BMI160_CHIP_ID_ADDR, &dev->chip_id, 1, dev); + } + + if((rslt == BMI160_OK) && (dev->chip_id == BMI160_CHIP_ID)) { + dev->any_sig_sel = BMI160_BOTH_ANY_SIG_MOTION_DISABLED; + + /* Soft reset */ + rslt = bmi160_soft_reset(dev); + } else { + rslt = BMI160_E_DEV_NOT_FOUND; + } + } + + return rslt; +} + +/*! + * @brief This API resets and restarts the device. + * All register values are overwritten with default parameters. + */ +int8_t bmi160_soft_reset(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = BMI160_SOFT_RESET_CMD; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Reset the device */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &data, 1, dev); + dev->delay_ms(BMI160_SOFT_RESET_DELAY_MS); + if((rslt == BMI160_OK) && (dev->intf == BMI160_SPI_INTF)) { + /* Dummy read of 0x7F register to enable SPI Interface + * if SPI is used */ + rslt = bmi160_get_regs(BMI160_SPI_COMM_TEST_ADDR, &data, 1, dev); + } + + if(rslt == BMI160_OK) { + /* Update the default parameters */ + default_param_settg(dev); + } + } + + return rslt; +} + +/*! + * @brief This API configures the power mode, range and bandwidth + * of sensor. + */ +int8_t bmi160_set_sens_conf(struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = set_accel_conf(dev); + if(rslt == BMI160_OK) { + rslt = set_gyro_conf(dev); + if(rslt == BMI160_OK) { + /* write power mode for accel and gyro */ + rslt = bmi160_set_power_mode(dev); + if(rslt == BMI160_OK) { + rslt = check_invalid_settg(dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API gets accel and gyro configurations. + */ +int8_t bmi160_get_sens_conf(struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = get_accel_conf(dev); + if(rslt == BMI160_OK) { + rslt = get_gyro_conf(dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the power mode of the sensor. + */ +int8_t bmi160_set_power_mode(struct bmi160_dev* dev) { + int8_t rslt = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = set_accel_pwr(dev); + if(rslt == BMI160_OK) { + rslt = set_gyro_pwr(dev); + } + } + + return rslt; +} + +/*! + * @brief This API gets the power mode of the sensor. + */ +int8_t bmi160_get_power_mode(struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t power_mode = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(BMI160_PMU_STATUS_ADDR, &power_mode, 1, dev); + if(rslt == BMI160_OK) { + /* Power mode of the accel, gyro sensor is obtained */ + dev->gyro_cfg.power = BMI160_GET_BITS(power_mode, BMI160_GYRO_POWER_MODE); + dev->accel_cfg.power = BMI160_GET_BITS(power_mode, BMI160_ACCEL_POWER_MODE); + } + } + + return rslt; +} + +/*! + * @brief This API reads sensor data, stores it in + * the bmi160_sensor_data structure pointer passed by the user. + */ +int8_t bmi160_get_sensor_data( + uint8_t select_sensor, + struct bmi160_sensor_data* accel, + struct bmi160_sensor_data* gyro, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint8_t time_sel; + uint8_t sen_sel; + uint8_t len = 0; + + /*Extract the sensor and time select information*/ + sen_sel = select_sensor & BMI160_SEN_SEL_MASK; + time_sel = ((sen_sel & BMI160_TIME_SEL) >> 2); + sen_sel = sen_sel & (BMI160_ACCEL_SEL | BMI160_GYRO_SEL); + if(time_sel == 1) { + len = 3; + } + + /* Null-pointer check */ + if(dev != NULL) { + switch(sen_sel) { + case BMI160_ACCEL_ONLY: + + /* Null-pointer check */ + if(accel == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = get_accel_data(len, accel, dev); + } + + break; + case BMI160_GYRO_ONLY: + + /* Null-pointer check */ + if(gyro == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = get_gyro_data(len, gyro, dev); + } + + break; + case BMI160_BOTH_ACCEL_AND_GYRO: + + /* Null-pointer check */ + if((gyro == NULL) || (accel == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = get_accel_gyro_data(len, accel, gyro, dev); + } + + break; + default: + rslt = BMI160_E_INVALID_INPUT; + break; + } + } else { + rslt = BMI160_E_NULL_PTR; + } + + return rslt; +} + +/*! + * @brief This API configures the necessary interrupt based on + * the user settings in the bmi160_int_settg structure instance. + */ +int8_t bmi160_set_int_config(struct bmi160_int_settg* int_config, struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + switch(int_config->int_type) { + case BMI160_ACC_ANY_MOTION_INT: + + /*Any-motion interrupt*/ + rslt = set_accel_any_motion_int(int_config, dev); + break; + case BMI160_ACC_SIG_MOTION_INT: + + /* Significant motion interrupt */ + rslt = set_accel_sig_motion_int(int_config, dev); + break; + case BMI160_ACC_SLOW_NO_MOTION_INT: + + /* Slow or no motion interrupt */ + rslt = set_accel_no_motion_int(int_config, dev); + break; + case BMI160_ACC_DOUBLE_TAP_INT: + case BMI160_ACC_SINGLE_TAP_INT: + + /* Double tap and single tap Interrupt */ + rslt = set_accel_tap_int(int_config, dev); + break; + case BMI160_STEP_DETECT_INT: + + /* Step detector interrupt */ + rslt = set_accel_step_detect_int(int_config, dev); + break; + case BMI160_ACC_ORIENT_INT: + + /* Orientation interrupt */ + rslt = set_accel_orientation_int(int_config, dev); + break; + case BMI160_ACC_FLAT_INT: + + /* Flat detection interrupt */ + rslt = set_accel_flat_detect_int(int_config, dev); + break; + case BMI160_ACC_LOW_G_INT: + + /* Low-g interrupt */ + rslt = set_accel_low_g_int(int_config, dev); + break; + case BMI160_ACC_HIGH_G_INT: + + /* High-g interrupt */ + rslt = set_accel_high_g_int(int_config, dev); + break; + case BMI160_ACC_GYRO_DATA_RDY_INT: + + /* Data ready interrupt */ + rslt = set_accel_gyro_data_ready_int(int_config, dev); + break; + case BMI160_ACC_GYRO_FIFO_FULL_INT: + + /* Fifo full interrupt */ + rslt = set_fifo_full_int(int_config, dev); + break; + case BMI160_ACC_GYRO_FIFO_WATERMARK_INT: + + /* Fifo water-mark interrupt */ + rslt = set_fifo_watermark_int(int_config, dev); + break; + case BMI160_FIFO_TAG_INT_PIN: + + /* Fifo tagging feature support */ + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + break; + default: + break; + } + + return rslt; +} + +/*! + * @brief This API enables or disable the step counter feature. + * 1 - enable step counter (0 - disable) + */ +int8_t bmi160_set_step_counter(uint8_t step_cnt_enable, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(BMI160_INT_STEP_CONFIG_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(step_cnt_enable == BMI160_ENABLE) { + data |= (uint8_t)(step_cnt_enable << 3); + } else { + data &= ~BMI160_STEP_COUNT_EN_BIT_MASK; + } + + rslt = bmi160_set_regs(BMI160_INT_STEP_CONFIG_1_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API reads the step counter value. + */ +int8_t bmi160_read_step_counter(uint16_t* step_val, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0, 0}; + uint16_t msb = 0; + uint8_t lsb = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(BMI160_INT_STEP_CNT_0_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + lsb = data[0]; + msb = data[1] << 8; + *step_val = msb | lsb; + } + } + + return rslt; +} + +/*! + * @brief This API reads the mention no of byte of data from the given + * register address of auxiliary sensor. + */ +int8_t bmi160_aux_read( + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint16_t map_len = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->read == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + if(dev->aux_cfg.aux_sensor_enable == BMI160_ENABLE) { + rslt = map_read_len(&map_len, dev); + if(rslt == BMI160_OK) { + rslt = extract_aux_read(map_len, reg_addr, aux_data, len, dev); + } + } else { + rslt = BMI160_E_INVALID_INPUT; + } + } + + return rslt; +} + +/*! + * @brief This API writes the mention no of byte of data to the given + * register address of auxiliary sensor. + */ +int8_t bmi160_aux_write( + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint8_t count = 0; + + /* Null-pointer check */ + if((dev == NULL) || (dev->write == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + for(; count < len; count++) { + /* set data to write */ + rslt = bmi160_set_regs(BMI160_AUX_IF_4_ADDR, aux_data, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + if(rslt == BMI160_OK) { + /* set address to write */ + rslt = bmi160_set_regs(BMI160_AUX_IF_3_ADDR, ®_addr, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + if(rslt == BMI160_OK && (count < len - 1)) { + aux_data++; + reg_addr++; + } + } + } + } + + return rslt; +} + +/*! + * @brief This API initialize the auxiliary sensor + * in order to access it. + */ +int8_t bmi160_aux_init(const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + if(dev->aux_cfg.aux_sensor_enable == BMI160_ENABLE) { + /* Configures the auxiliary sensor interface settings */ + rslt = config_aux_settg(dev); + } else { + rslt = BMI160_E_INVALID_INPUT; + } + } + + return rslt; +} + +/*! + * @brief This API is used to setup the auxiliary sensor of bmi160 in auto mode + * Thus enabling the auto update of 8 bytes of data from auxiliary sensor + * to BMI160 register address 0x04 to 0x0B + */ +int8_t bmi160_set_aux_auto_mode(uint8_t* data_addr, struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + if(dev->aux_cfg.aux_sensor_enable == BMI160_ENABLE) { + /* Write the aux. address to read in 0x4D of BMI160*/ + rslt = bmi160_set_regs(BMI160_AUX_IF_2_ADDR, data_addr, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + if(rslt == BMI160_OK) { + /* Configure the polling ODR for + * auxiliary sensor */ + rslt = config_aux_odr(dev); + if(rslt == BMI160_OK) { + /* Disable the aux. manual mode, i.e aux. + * sensor is in auto-mode (data-mode) */ + dev->aux_cfg.manual_enable = BMI160_DISABLE; + rslt = bmi160_config_aux_mode(dev); + + /* Auxiliary sensor data is obtained + * in auto mode from this point */ + } + } + } else { + rslt = BMI160_E_INVALID_INPUT; + } + } + + return rslt; +} + +/*! + * @brief This API configures the 0x4C register and settings like + * Auxiliary sensor manual enable/ disable and aux burst read length. + */ +int8_t bmi160_config_aux_mode(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t aux_if[2] = {(uint8_t)(dev->aux_cfg.aux_i2c_addr * 2), 0}; + + rslt = bmi160_get_regs(BMI160_AUX_IF_1_ADDR, &aux_if[1], 1, dev); + if(rslt == BMI160_OK) { + /* update the Auxiliary interface to manual/auto mode */ + aux_if[1] = BMI160_SET_BITS(aux_if[1], BMI160_MANUAL_MODE_EN, dev->aux_cfg.manual_enable); + + /* update the burst read length defined by user */ + aux_if[1] = + BMI160_SET_BITS_POS_0(aux_if[1], BMI160_AUX_READ_BURST, dev->aux_cfg.aux_rd_burst_len); + + /* Set the secondary interface address and manual mode + * along with burst read length */ + rslt = bmi160_set_regs(BMI160_AUX_IF_0_ADDR, &aux_if[0], 2, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + } + + return rslt; +} + +/*! + * @brief This API is used to read the raw uncompensated auxiliary sensor + * data of 8 bytes from BMI160 register address 0x04 to 0x0B + */ +int8_t bmi160_read_aux_data_auto_mode(uint8_t* aux_data, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + if((dev->aux_cfg.aux_sensor_enable == BMI160_ENABLE) && + (dev->aux_cfg.manual_enable == BMI160_DISABLE)) { + /* Read the aux. sensor's raw data */ + rslt = bmi160_get_regs(BMI160_AUX_DATA_ADDR, aux_data, 8, dev); + } else { + rslt = BMI160_E_INVALID_INPUT; + } + } + + return rslt; +} + +/*! + * @brief This is used to perform self test of accel/gyro of the BMI160 sensor + */ +int8_t bmi160_perform_self_test(uint8_t select_sensor, struct bmi160_dev* dev) { + int8_t rslt; + int8_t self_test_rslt = 0; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Proceed if null check is fine */ + switch(select_sensor) { + case BMI160_ACCEL_ONLY: + rslt = perform_accel_self_test(dev); + break; + case BMI160_GYRO_ONLY: + + /* Set the power mode as normal mode */ + dev->gyro_cfg.power = BMI160_GYRO_NORMAL_MODE; + rslt = bmi160_set_power_mode(dev); + + /* Perform gyro self test */ + if(rslt == BMI160_OK) { + /* Perform gyro self test */ + rslt = perform_gyro_self_test(dev); + } + + break; + default: + rslt = BMI160_E_INVALID_INPUT; + break; + } + + /* Check to ensure bus error does not occur */ + if(rslt >= BMI160_OK) { + /* Store the status of self test result */ + self_test_rslt = rslt; + + /* Perform soft reset */ + rslt = bmi160_soft_reset(dev); + } + + /* Check to ensure bus operations are success */ + if(rslt == BMI160_OK) { + /* Restore self_test_rslt as return value */ + rslt = self_test_rslt; + } + } + + return rslt; +} + +/*! + * @brief This API reads the data from fifo buffer. + */ +int8_t bmi160_get_fifo_data(struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint16_t bytes_to_read = 0; + uint16_t user_fifo_len = 0; + + /* check the bmi160 structure as NULL*/ + if((dev == NULL) || (dev->fifo->data == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + reset_fifo_data_structure(dev); + + /* get current FIFO fill-level*/ + rslt = get_fifo_byte_counter(&bytes_to_read, dev); + if(rslt == BMI160_OK) { + user_fifo_len = dev->fifo->length; + if((dev->fifo->length > bytes_to_read)) { + /* Handling the case where user requests + * more data than available in FIFO */ + dev->fifo->length = bytes_to_read; + } + + if((dev->fifo->fifo_time_enable == BMI160_FIFO_TIME_ENABLE) && + (bytes_to_read + BMI160_FIFO_BYTES_OVERREAD <= user_fifo_len)) { + /* Handling case of sensor time availability*/ + dev->fifo->length = dev->fifo->length + BMI160_FIFO_BYTES_OVERREAD; + } + + /* read only the filled bytes in the FIFO Buffer */ + rslt = bmi160_get_regs(BMI160_FIFO_DATA_ADDR, dev->fifo->data, dev->fifo->length, dev); + } + } + + return rslt; +} + +/*! + * @brief This API writes fifo_flush command to command register.This + * action clears all data in the Fifo without changing fifo configuration + * settings + */ +int8_t bmi160_set_fifo_flush(const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t data = BMI160_FIFO_FLUSH_VALUE; + uint8_t reg_addr = BMI160_COMMAND_REG_ADDR; + + /* Check the bmi160_dev structure for NULL address*/ + if(dev == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_set_regs(reg_addr, &data, BMI160_ONE, dev); + } + + return rslt; +} + +/*! + * @brief This API sets the FIFO configuration in the sensor. + */ +int8_t bmi160_set_fifo_config(uint8_t config, uint8_t enable, struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint8_t data = 0; + uint8_t reg_addr = BMI160_FIFO_CONFIG_1_ADDR; + uint8_t fifo_config = config & BMI160_FIFO_CONFIG_1_MASK; + + /* Check the bmi160_dev structure for NULL address*/ + if(dev == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(reg_addr, &data, BMI160_ONE, dev); + if(rslt == BMI160_OK) { + if(fifo_config > 0) { + if(enable == BMI160_ENABLE) { + data = data | fifo_config; + } else { + data = data & (~fifo_config); + } + } + + /* write fifo frame content configuration*/ + rslt = bmi160_set_regs(reg_addr, &data, BMI160_ONE, dev); + if(rslt == BMI160_OK) { + /* read fifo frame content configuration*/ + rslt = bmi160_get_regs(reg_addr, &data, BMI160_ONE, dev); + if(rslt == BMI160_OK) { + /* extract fifo header enabled status */ + dev->fifo->fifo_header_enable = data & BMI160_FIFO_HEAD_ENABLE; + + /* extract accel/gyr/aux. data enabled status */ + dev->fifo->fifo_data_enable = data & BMI160_FIFO_M_G_A_ENABLE; + + /* extract fifo sensor time enabled status */ + dev->fifo->fifo_time_enable = data & BMI160_FIFO_TIME_ENABLE; + } + } + } + } + + return rslt; +} + +/*! @brief This API is used to configure the down sampling ratios of + * the accel and gyro data for FIFO.Also, it configures filtered or + * pre-filtered data for accel and gyro. + * + */ +int8_t bmi160_set_fifo_down(uint8_t fifo_down, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t data = 0; + uint8_t reg_addr = BMI160_FIFO_DOWN_ADDR; + + /* Check the bmi160_dev structure for NULL address*/ + if(dev == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_get_regs(reg_addr, &data, BMI160_ONE, dev); + if(rslt == BMI160_OK) { + data = data | fifo_down; + rslt = bmi160_set_regs(reg_addr, &data, BMI160_ONE, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the FIFO watermark level in the sensor. + * + */ +int8_t bmi160_set_fifo_wm(uint8_t fifo_wm, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t data = fifo_wm; + uint8_t reg_addr = BMI160_FIFO_CONFIG_0_ADDR; + + /* Check the bmi160_dev structure for NULL address*/ + if(dev == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = bmi160_set_regs(reg_addr, &data, BMI160_ONE, dev); + } + + return rslt; +} + +/*! + * @brief This API parses and extracts the accelerometer frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "accel_data" structure instance. + */ +int8_t bmi160_extract_accel( + struct bmi160_sensor_data* accel_data, + uint8_t* accel_length, + struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint16_t data_index = 0; + uint16_t data_read_length = 0; + uint8_t accel_index = 0; + uint8_t fifo_data_enable = 0; + + if(dev == NULL || dev->fifo == NULL || dev->fifo->data == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Parsing the FIFO data in header-less mode */ + if(dev->fifo->fifo_header_enable == 0) { + /* Number of bytes to be parsed from FIFO */ + get_accel_len_to_parse(&data_index, &data_read_length, accel_length, dev); + for(; data_index < data_read_length;) { + /*Check for the availability of next two bytes of FIFO data */ + check_frame_validity(&data_index, dev); + fifo_data_enable = dev->fifo->fifo_data_enable; + unpack_accel_frame(accel_data, &data_index, &accel_index, fifo_data_enable, dev); + } + + /* update number of accel data read*/ + *accel_length = accel_index; + + /*update the accel byte index*/ + dev->fifo->accel_byte_start_idx = data_index; + } else { + /* Parsing the FIFO data in header mode */ + extract_accel_header_mode(accel_data, accel_length, dev); + } + } + + return rslt; +} + +/*! + * @brief This API parses and extracts the gyro frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "gyro_data" structure instance. + */ +int8_t bmi160_extract_gyro( + struct bmi160_sensor_data* gyro_data, + uint8_t* gyro_length, + struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint16_t data_index = 0; + uint16_t data_read_length = 0; + uint8_t gyro_index = 0; + uint8_t fifo_data_enable = 0; + + if(dev == NULL || dev->fifo->data == NULL) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Parsing the FIFO data in header-less mode */ + if(dev->fifo->fifo_header_enable == 0) { + /* Number of bytes to be parsed from FIFO */ + get_gyro_len_to_parse(&data_index, &data_read_length, gyro_length, dev); + for(; data_index < data_read_length;) { + /*Check for the availability of next two bytes of FIFO data */ + check_frame_validity(&data_index, dev); + fifo_data_enable = dev->fifo->fifo_data_enable; + unpack_gyro_frame(gyro_data, &data_index, &gyro_index, fifo_data_enable, dev); + } + + /* update number of gyro data read */ + *gyro_length = gyro_index; + + /* update the gyro byte index */ + dev->fifo->gyro_byte_start_idx = data_index; + } else { + /* Parsing the FIFO data in header mode */ + extract_gyro_header_mode(gyro_data, gyro_length, dev); + } + } + + return rslt; +} + +/*! + * @brief This API parses and extracts the aux frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "aux_data" structure instance. + */ +int8_t bmi160_extract_aux( + struct bmi160_aux_data* aux_data, + uint8_t* aux_len, + struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint16_t data_index = 0; + uint16_t data_read_length = 0; + uint8_t aux_index = 0; + uint8_t fifo_data_enable = 0; + + if((dev == NULL) || (dev->fifo->data == NULL) || (aux_data == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Parsing the FIFO data in header-less mode */ + if(dev->fifo->fifo_header_enable == 0) { + /* Number of bytes to be parsed from FIFO */ + get_aux_len_to_parse(&data_index, &data_read_length, aux_len, dev); + for(; data_index < data_read_length;) { + /* Check for the availability of next two + * bytes of FIFO data */ + check_frame_validity(&data_index, dev); + fifo_data_enable = dev->fifo->fifo_data_enable; + unpack_aux_frame(aux_data, &data_index, &aux_index, fifo_data_enable, dev); + } + + /* update number of aux data read */ + *aux_len = aux_index; + + /* update the aux byte index */ + dev->fifo->aux_byte_start_idx = data_index; + } else { + /* Parsing the FIFO data in header mode */ + extract_aux_header_mode(aux_data, aux_len, dev); + } + } + + return rslt; +} + +/*! + * @brief This API starts the FOC of accel and gyro + * + * @note FOC should not be used in low-power mode of sensor + * + * @note Accel FOC targets values of +1g , 0g , -1g + * Gyro FOC always targets value of 0 dps + */ +int8_t bmi160_start_foc( + const struct bmi160_foc_conf* foc_conf, + struct bmi160_offsets* offset, + struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Set the offset enable bits */ + rslt = configure_offset_enable(foc_conf, dev); + if(rslt == BMI160_OK) { + /* Read the FOC config from the sensor */ + rslt = bmi160_get_regs(BMI160_FOC_CONF_ADDR, &data, 1, dev); + + /* Set the FOC config for gyro */ + data = BMI160_SET_BITS(data, BMI160_GYRO_FOC_EN, foc_conf->foc_gyr_en); + + /* Set the FOC config for accel xyz axes */ + data = BMI160_SET_BITS(data, BMI160_ACCEL_FOC_X_CONF, foc_conf->foc_acc_x); + data = BMI160_SET_BITS(data, BMI160_ACCEL_FOC_Y_CONF, foc_conf->foc_acc_y); + data = BMI160_SET_BITS_POS_0(data, BMI160_ACCEL_FOC_Z_CONF, foc_conf->foc_acc_z); + if(rslt == BMI160_OK) { + /* Set the FOC config in the sensor */ + rslt = bmi160_set_regs(BMI160_FOC_CONF_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Procedure to trigger + * FOC and check status */ + rslt = trigger_foc(offset, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API reads and stores the offset values of accel and gyro + */ +int8_t bmi160_get_offsets(struct bmi160_offsets* offset, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[7]; + uint8_t lsb, msb; + int16_t offset_msb, offset_lsb; + int16_t offset_data; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Read the FOC config from the sensor */ + rslt = bmi160_get_regs(BMI160_OFFSET_ADDR, data, 7, dev); + + /* Accel offsets */ + offset->off_acc_x = (int8_t)data[0]; + offset->off_acc_y = (int8_t)data[1]; + offset->off_acc_z = (int8_t)data[2]; + + /* Gyro x-axis offset */ + lsb = data[3]; + msb = BMI160_GET_BITS_POS_0(data[6], BMI160_GYRO_OFFSET_X); + offset_msb = (int16_t)(msb << 14); + offset_lsb = lsb << 6; + offset_data = offset_msb | offset_lsb; + + /* Divide by 64 to get the Right shift by 6 value */ + offset->off_gyro_x = (int16_t)(offset_data / 64); + + /* Gyro y-axis offset */ + lsb = data[4]; + msb = BMI160_GET_BITS(data[6], BMI160_GYRO_OFFSET_Y); + offset_msb = (int16_t)(msb << 14); + offset_lsb = lsb << 6; + offset_data = offset_msb | offset_lsb; + + /* Divide by 64 to get the Right shift by 6 value */ + offset->off_gyro_y = (int16_t)(offset_data / 64); + + /* Gyro z-axis offset */ + lsb = data[5]; + msb = BMI160_GET_BITS(data[6], BMI160_GYRO_OFFSET_Z); + offset_msb = (int16_t)(msb << 14); + offset_lsb = lsb << 6; + offset_data = offset_msb | offset_lsb; + + /* Divide by 64 to get the Right shift by 6 value */ + offset->off_gyro_z = (int16_t)(offset_data / 64); + } + + return rslt; +} + +/*! + * @brief This API writes the offset values of accel and gyro to + * the sensor but these values will be reset on POR or soft reset. + */ +int8_t bmi160_set_offsets( + const struct bmi160_foc_conf* foc_conf, + const struct bmi160_offsets* offset, + struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data[7]; + uint8_t x_msb, y_msb, z_msb; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Update the accel offset */ + data[0] = (uint8_t)offset->off_acc_x; + data[1] = (uint8_t)offset->off_acc_y; + data[2] = (uint8_t)offset->off_acc_z; + + /* Update the LSB of gyro offset */ + data[3] = BMI160_GET_LSB(offset->off_gyro_x); + data[4] = BMI160_GET_LSB(offset->off_gyro_y); + data[5] = BMI160_GET_LSB(offset->off_gyro_z); + + /* Update the MSB of gyro offset */ + x_msb = BMI160_GET_BITS(offset->off_gyro_x, BMI160_GYRO_OFFSET); + y_msb = BMI160_GET_BITS(offset->off_gyro_y, BMI160_GYRO_OFFSET); + z_msb = BMI160_GET_BITS(offset->off_gyro_z, BMI160_GYRO_OFFSET); + data[6] = (uint8_t)(z_msb << 4 | y_msb << 2 | x_msb); + + /* Set the offset enable/disable for gyro and accel */ + data[6] = BMI160_SET_BITS(data[6], BMI160_GYRO_OFFSET_EN, foc_conf->gyro_off_en); + data[6] = BMI160_SET_BITS(data[6], BMI160_ACCEL_OFFSET_EN, foc_conf->acc_off_en); + + /* Set the offset config and values in the sensor */ + rslt = bmi160_set_regs(BMI160_OFFSET_ADDR, data, 7, dev); + } + + return rslt; +} + +/*! + * @brief This API writes the image registers values to NVM which is + * stored even after POR or soft reset + */ +int8_t bmi160_update_nvm(struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data; + uint8_t cmd = BMI160_NVM_BACKUP_EN; + + /* Read the nvm_prog_en configuration */ + rslt = bmi160_get_regs(BMI160_CONF_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + data = BMI160_SET_BITS(data, BMI160_NVM_UPDATE, 1); + + /* Set the nvm_prog_en bit in the sensor */ + rslt = bmi160_set_regs(BMI160_CONF_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Update NVM */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &cmd, 1, dev); + if(rslt == BMI160_OK) { + /* Check for NVM ready status */ + rslt = bmi160_get_regs(BMI160_STATUS_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + data = BMI160_GET_BITS(data, BMI160_NVM_STATUS); + if(data != BMI160_ENABLE) { + /* Delay to update NVM */ + dev->delay_ms(25); + } + } + } + } + } + + return rslt; +} + +/*! + * @brief This API gets the interrupt status from the sensor. + */ +int8_t bmi160_get_int_status( + enum bmi160_int_status_sel int_status_sel, + union bmi160_int_status* int_status, + struct bmi160_dev const* dev) { + int8_t rslt = 0; + + /* To get the status of all interrupts */ + if(int_status_sel == BMI160_INT_STATUS_ALL) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR, &int_status->data[0], 4, dev); + } else { + if(int_status_sel & BMI160_INT_STATUS_0) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR, &int_status->data[0], 1, dev); + } + + if(int_status_sel & BMI160_INT_STATUS_1) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR + 1, &int_status->data[1], 1, dev); + } + + if(int_status_sel & BMI160_INT_STATUS_2) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR + 2, &int_status->data[2], 1, dev); + } + + if(int_status_sel & BMI160_INT_STATUS_3) { + rslt = bmi160_get_regs(BMI160_INT_STATUS_ADDR + 3, &int_status->data[3], 1, dev); + } + } + + return rslt; +} + +/*********************** Local function definitions ***************************/ + +/*! + * @brief This API sets the any-motion interrupt of the sensor. + * This interrupt occurs when accel values exceeds preset threshold + * for a certain period of time. + */ +static int8_t + set_accel_any_motion_int(struct bmi160_int_settg* int_config, struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg = + &(int_config->int_type_cfg.acc_any_motion_int); + rslt = enable_accel_any_motion_int(any_motion_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_any_motion_int_settg(int_config, any_motion_int_cfg, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets tap interrupts.Interrupt is fired when + * tap movements happen. + */ +static int8_t + set_accel_tap_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_tap_int_cfg* tap_int_cfg = &(int_config->int_type_cfg.acc_tap_int); + rslt = enable_tap_int(int_config, tap_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_tap_int_settg(int_config, tap_int_cfg, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the data ready interrupt for both accel and gyro. + * This interrupt occurs when new accel and gyro data comes. + */ +static int8_t set_accel_gyro_data_ready_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + rslt = enable_data_ready_int(dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_hardware_interrupt(int_config, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the significant motion interrupt of the sensor.This + * interrupt occurs when there is change in user location. + */ +static int8_t + set_accel_sig_motion_int(struct bmi160_int_settg* int_config, struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg = + &(int_config->int_type_cfg.acc_sig_motion_int); + rslt = enable_sig_motion_int(sig_mot_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_sig_motion_int_settg(int_config, sig_mot_int_cfg, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the no motion/slow motion interrupt of the sensor. + * Slow motion is similar to any motion interrupt.No motion interrupt + * occurs when slope bet. two accel values falls below preset threshold + * for preset duration. + */ +static int8_t + set_accel_no_motion_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg = + &(int_config->int_type_cfg.acc_no_motion_int); + rslt = enable_no_motion_int(no_mot_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure the INT PIN settings*/ + rslt = config_no_motion_int_settg(int_config, no_mot_int_cfg, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the step detection interrupt.This interrupt + * occurs when the single step causes accel values to go above + * preset threshold. + */ +static int8_t + set_accel_step_detect_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg = + &(int_config->int_type_cfg.acc_step_detect_int); + rslt = enable_step_detect_int(step_detect_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_step_detect(step_detect_int_cfg, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the orientation interrupt of the sensor.This + * interrupt occurs when there is orientation change in the sensor + * with respect to gravitational field vector g. + */ +static int8_t + set_accel_orientation_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_orient_int_cfg* orient_int_cfg = + &(int_config->int_type_cfg.acc_orient_int); + rslt = enable_orient_int(orient_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + /* map INT pin to orient interrupt */ + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + /* configure the + * orientation setting*/ + rslt = config_orient_int_settg(orient_int_cfg, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the flat interrupt of the sensor.This interrupt + * occurs in case of flat orientation + */ +static int8_t + set_accel_flat_detect_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_flat_detect_int_cfg* flat_detect_int = + &(int_config->int_type_cfg.acc_flat_int); + + /* enable the flat interrupt */ + rslt = enable_flat_int(flat_detect_int, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + /* map INT pin to flat interrupt */ + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + /* configure the flat setting*/ + rslt = config_flat_int_settg(flat_detect_int, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the low-g interrupt of the sensor.This interrupt + * occurs during free-fall. + */ +static int8_t + set_accel_low_g_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_low_g_int_cfg* low_g_int = &(int_config->int_type_cfg.acc_low_g_int); + + /* Enable the low-g interrupt*/ + rslt = enable_low_g_int(low_g_int, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + /* Map INT pin to low-g interrupt */ + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + /* configure the data source + * for low-g interrupt*/ + rslt = config_low_g_data_src(low_g_int, dev); + if(rslt == BMI160_OK) { + rslt = config_low_g_int_settg(low_g_int, dev); + } + } + } + } + } + + return rslt; +} + +/*! + * @brief This API sets the high-g interrupt of the sensor.The interrupt + * occurs if the absolute value of acceleration data of any enabled axis + * exceeds the programmed threshold and the sign of the value does not + * change for a preset duration. + */ +static int8_t + set_accel_high_g_int(struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if((rslt != BMI160_OK) || (int_config == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* updating the interrupt structure to local structure */ + struct bmi160_acc_high_g_int_cfg* high_g_int_cfg = + &(int_config->int_type_cfg.acc_high_g_int); + + /* Enable the high-g interrupt */ + rslt = enable_high_g_int(high_g_int_cfg, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + /* Map INT pin to high-g interrupt */ + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + /* configure the data source + * for high-g interrupt*/ + rslt = config_high_g_data_src(high_g_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_high_g_int_settg(high_g_int_cfg, dev); + } + } + } + } + } + + return rslt; +} + +/*! + * @brief This API configures the pins to fire the + * interrupt signal when it occurs. + */ +static int8_t + set_intr_pin_config(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + + /* configure the behavioural settings of interrupt pin */ + rslt = config_int_out_ctrl(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_int_latch(int_config, dev); + } + + return rslt; +} + +/*! + * @brief This internal API is used to validate the device structure pointer for + * null conditions. + */ +static int8_t null_ptr_check(const struct bmi160_dev* dev) { + int8_t rslt; + + if((dev == NULL) || (dev->read == NULL) || (dev->write == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Device structure is fine */ + rslt = BMI160_OK; + } + + return rslt; +} + +/*! + * @brief This API sets the default configuration parameters of accel & gyro. + * Also maintain the previous state of configurations. + */ +static void default_param_settg(struct bmi160_dev* dev) { + /* Initializing accel and gyro params with + * default values */ + dev->accel_cfg.bw = BMI160_ACCEL_BW_NORMAL_AVG4; + dev->accel_cfg.odr = BMI160_ACCEL_ODR_100HZ; + dev->accel_cfg.power = BMI160_ACCEL_SUSPEND_MODE; + dev->accel_cfg.range = BMI160_ACCEL_RANGE_2G; + dev->gyro_cfg.bw = BMI160_GYRO_BW_NORMAL_MODE; + dev->gyro_cfg.odr = BMI160_GYRO_ODR_100HZ; + dev->gyro_cfg.power = BMI160_GYRO_SUSPEND_MODE; + dev->gyro_cfg.range = BMI160_GYRO_RANGE_2000_DPS; + + /* To maintain the previous state of accel configuration */ + dev->prev_accel_cfg = dev->accel_cfg; + + /* To maintain the previous state of gyro configuration */ + dev->prev_gyro_cfg = dev->gyro_cfg; +} + +/*! + * @brief This API set the accel configuration. + */ +static int8_t set_accel_conf(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0}; + + rslt = check_accel_config(data, dev); + if(rslt == BMI160_OK) { + /* Write output data rate and bandwidth */ + rslt = bmi160_set_regs(BMI160_ACCEL_CONFIG_ADDR, &data[0], 1, dev); + if(rslt == BMI160_OK) { + dev->prev_accel_cfg.odr = dev->accel_cfg.odr; + dev->prev_accel_cfg.bw = dev->accel_cfg.bw; + + /* write accel range */ + rslt = bmi160_set_regs(BMI160_ACCEL_RANGE_ADDR, &data[1], 1, dev); + if(rslt == BMI160_OK) { + dev->prev_accel_cfg.range = dev->accel_cfg.range; + } + } + } + + return rslt; +} + +/*! + * @brief This API gets the accel configuration. + */ +static int8_t get_accel_conf(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0}; + + /* Get accel configurations */ + rslt = bmi160_get_regs(BMI160_ACCEL_CONFIG_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + dev->accel_cfg.odr = (data[0] & BMI160_ACCEL_ODR_MASK); + dev->accel_cfg.bw = (data[0] & BMI160_ACCEL_BW_MASK) >> BMI160_ACCEL_BW_POS; + dev->accel_cfg.range = (data[1] & BMI160_ACCEL_RANGE_MASK); + } + + return rslt; +} + +/*! + * @brief This API check the accel configuration. + */ +static int8_t check_accel_config(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt; + + /* read accel Output data rate and bandwidth */ + rslt = bmi160_get_regs(BMI160_ACCEL_CONFIG_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + rslt = process_accel_odr(&data[0], dev); + if(rslt == BMI160_OK) { + rslt = process_accel_bw(&data[0], dev); + if(rslt == BMI160_OK) { + rslt = process_accel_range(&data[1], dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API process the accel odr. + */ +static int8_t process_accel_odr(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t odr = 0; + + if(dev->accel_cfg.odr <= BMI160_ACCEL_ODR_1600HZ) { + if(dev->accel_cfg.odr != dev->prev_accel_cfg.odr) { + odr = (uint8_t)dev->accel_cfg.odr; + temp = *data & ~BMI160_ACCEL_ODR_MASK; + + /* Adding output data rate */ + *data = temp | (odr & BMI160_ACCEL_ODR_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API process the accel bandwidth. + */ +static int8_t process_accel_bw(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t bw = 0; + + if(dev->accel_cfg.bw <= BMI160_ACCEL_BW_RES_AVG128) { + if(dev->accel_cfg.bw != dev->prev_accel_cfg.bw) { + bw = (uint8_t)dev->accel_cfg.bw; + temp = *data & ~BMI160_ACCEL_BW_MASK; + + /* Adding bandwidth */ + *data = temp | ((bw << 4) & BMI160_ACCEL_BW_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API process the accel range. + */ +static int8_t process_accel_range(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t range = 0; + + if(dev->accel_cfg.range <= BMI160_ACCEL_RANGE_16G) { + if(dev->accel_cfg.range != dev->prev_accel_cfg.range) { + range = (uint8_t)dev->accel_cfg.range; + temp = *data & ~BMI160_ACCEL_RANGE_MASK; + + /* Adding range */ + *data = temp | (range & BMI160_ACCEL_RANGE_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API checks the invalid settings for ODR & Bw for + * Accel and Gyro. + */ +static int8_t check_invalid_settg(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + + /* read the error reg */ + rslt = bmi160_get_regs(BMI160_ERROR_REG_ADDR, &data, 1, dev); + data = data >> 1; + data = data & BMI160_ERR_REG_MASK; + if(data == 1) { + rslt = BMI160_E_ACCEL_ODR_BW_INVALID; + } else if(data == 2) { + rslt = BMI160_E_GYRO_ODR_BW_INVALID; + } else if(data == 3) { + rslt = BMI160_E_LWP_PRE_FLTR_INT_INVALID; + } else if(data == 7) { + rslt = BMI160_E_LWP_PRE_FLTR_INVALID; + } + + return rslt; +} +static int8_t set_gyro_conf(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0}; + + rslt = check_gyro_config(data, dev); + if(rslt == BMI160_OK) { + /* Write output data rate and bandwidth */ + rslt = bmi160_set_regs(BMI160_GYRO_CONFIG_ADDR, &data[0], 1, dev); + if(rslt == BMI160_OK) { + dev->prev_gyro_cfg.odr = dev->gyro_cfg.odr; + dev->prev_gyro_cfg.bw = dev->gyro_cfg.bw; + + /* Write gyro range */ + rslt = bmi160_set_regs(BMI160_GYRO_RANGE_ADDR, &data[1], 1, dev); + if(rslt == BMI160_OK) { + dev->prev_gyro_cfg.range = dev->gyro_cfg.range; + } + } + } + + return rslt; +} + +/*! + * @brief This API gets the gyro configuration. + */ +static int8_t get_gyro_conf(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[2] = {0}; + + /* Get accel configurations */ + rslt = bmi160_get_regs(BMI160_GYRO_CONFIG_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + dev->gyro_cfg.odr = (data[0] & BMI160_GYRO_ODR_MASK); + dev->gyro_cfg.bw = (data[0] & BMI160_GYRO_BW_MASK) >> BMI160_GYRO_BW_POS; + dev->gyro_cfg.range = (data[1] & BMI160_GYRO_RANGE_MASK); + } + + return rslt; +} + +/*! + * @brief This API check the gyro configuration. + */ +static int8_t check_gyro_config(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt; + + /* read gyro Output data rate and bandwidth */ + rslt = bmi160_get_regs(BMI160_GYRO_CONFIG_ADDR, data, 2, dev); + if(rslt == BMI160_OK) { + rslt = process_gyro_odr(&data[0], dev); + if(rslt == BMI160_OK) { + rslt = process_gyro_bw(&data[0], dev); + if(rslt == BMI160_OK) { + rslt = process_gyro_range(&data[1], dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API process the gyro odr. + */ +static int8_t process_gyro_odr(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t odr = 0; + + if(dev->gyro_cfg.odr <= BMI160_GYRO_ODR_3200HZ) { + if(dev->gyro_cfg.odr != dev->prev_gyro_cfg.odr) { + odr = (uint8_t)dev->gyro_cfg.odr; + temp = (*data & ~BMI160_GYRO_ODR_MASK); + + /* Adding output data rate */ + *data = temp | (odr & BMI160_GYRO_ODR_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API process the gyro bandwidth. + */ +static int8_t process_gyro_bw(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t bw = 0; + + if(dev->gyro_cfg.bw <= BMI160_GYRO_BW_NORMAL_MODE) { + bw = (uint8_t)dev->gyro_cfg.bw; + temp = *data & ~BMI160_GYRO_BW_MASK; + + /* Adding bandwidth */ + *data = temp | ((bw << 4) & BMI160_GYRO_BW_MASK); + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API process the gyro range. + */ +static int8_t process_gyro_range(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t temp = 0; + uint8_t range = 0; + + if(dev->gyro_cfg.range <= BMI160_GYRO_RANGE_125_DPS) { + if(dev->gyro_cfg.range != dev->prev_gyro_cfg.range) { + range = (uint8_t)dev->gyro_cfg.range; + temp = *data & ~BMI160_GYRO_RANGE_MASK; + + /* Adding range */ + *data = temp | (range & BMI160_GYRO_RANGE_MASK); + } + } else { + rslt = BMI160_E_OUT_OF_RANGE; + } + + return rslt; +} + +/*! + * @brief This API sets the accel power. + */ +static int8_t set_accel_pwr(struct bmi160_dev* dev) { + int8_t rslt = 0; + uint8_t data = 0; + + if((dev->accel_cfg.power >= BMI160_ACCEL_SUSPEND_MODE) && + (dev->accel_cfg.power <= BMI160_ACCEL_LOWPOWER_MODE)) { + if(dev->accel_cfg.power != dev->prev_accel_cfg.power) { + rslt = process_under_sampling(&data, dev); + if(rslt == BMI160_OK) { + /* Write accel power */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &dev->accel_cfg.power, 1, dev); + + /* Add delay of 3.8 ms - refer data sheet table 24*/ + if(dev->prev_accel_cfg.power == BMI160_ACCEL_SUSPEND_MODE) { + dev->delay_ms(BMI160_ACCEL_DELAY_MS); + } + + dev->prev_accel_cfg.power = dev->accel_cfg.power; + } + } + } else { + rslt = BMI160_E_INVALID_CONFIG; + } + + return rslt; +} + +/*! + * @brief This API process the undersampling setting of Accel. + */ +static int8_t process_under_sampling(uint8_t* data, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t pre_filter[2] = {0}; + + rslt = bmi160_get_regs(BMI160_ACCEL_CONFIG_ADDR, data, 1, dev); + if(rslt == BMI160_OK) { + if(dev->accel_cfg.power == BMI160_ACCEL_LOWPOWER_MODE) { + temp = *data & ~BMI160_ACCEL_UNDERSAMPLING_MASK; + + /* Set under-sampling parameter */ + *data = temp | ((1 << 7) & BMI160_ACCEL_UNDERSAMPLING_MASK); + + /* Write data */ + rslt = bmi160_set_regs(BMI160_ACCEL_CONFIG_ADDR, data, 1, dev); + + /* Disable the pre-filter data in low power mode */ + if(rslt == BMI160_OK) { + /* Disable the Pre-filter data*/ + rslt = bmi160_set_regs(BMI160_INT_DATA_0_ADDR, pre_filter, 2, dev); + } + } else if(*data & BMI160_ACCEL_UNDERSAMPLING_MASK) { + temp = *data & ~BMI160_ACCEL_UNDERSAMPLING_MASK; + + /* Disable under-sampling parameter if already enabled */ + *data = temp; + + /* Write data */ + rslt = bmi160_set_regs(BMI160_ACCEL_CONFIG_ADDR, data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API sets the gyro power mode. + */ +static int8_t set_gyro_pwr(struct bmi160_dev* dev) { + int8_t rslt = 0; + + if((dev->gyro_cfg.power == BMI160_GYRO_SUSPEND_MODE) || + (dev->gyro_cfg.power == BMI160_GYRO_NORMAL_MODE) || + (dev->gyro_cfg.power == BMI160_GYRO_FASTSTARTUP_MODE)) { + if(dev->gyro_cfg.power != dev->prev_gyro_cfg.power) { + /* Write gyro power */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &dev->gyro_cfg.power, 1, dev); + if(dev->prev_gyro_cfg.power == BMI160_GYRO_SUSPEND_MODE) { + /* Delay of 80 ms - datasheet Table 24 */ + dev->delay_ms(BMI160_GYRO_DELAY_MS); + } else if( + (dev->prev_gyro_cfg.power == BMI160_GYRO_FASTSTARTUP_MODE) && + (dev->gyro_cfg.power == BMI160_GYRO_NORMAL_MODE)) { + /* This delay is required for transition from + * fast-startup mode to normal mode - datasheet Table 3 */ + dev->delay_ms(10); + } else { + /* do nothing */ + } + + dev->prev_gyro_cfg.power = dev->gyro_cfg.power; + } + } else { + rslt = BMI160_E_INVALID_CONFIG; + } + + return rslt; +} + +/*! + * @brief This API reads accel data along with sensor time if time is requested + * by user. Kindly refer the user guide(README.md) for more info. + */ +static int8_t + get_accel_data(uint8_t len, struct bmi160_sensor_data* accel, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t idx = 0; + uint8_t data_array[9] = {0}; + uint8_t time_0 = 0; + uint16_t time_1 = 0; + uint32_t time_2 = 0; + uint8_t lsb; + uint8_t msb; + int16_t msblsb; + + /* read accel sensor data along with time if requested */ + rslt = bmi160_get_regs(BMI160_ACCEL_DATA_ADDR, data_array, 6 + len, dev); + if(rslt == BMI160_OK) { + /* Accel Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->x = msblsb; /* Data in X axis */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->y = msblsb; /* Data in Y axis */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->z = msblsb; /* Data in Z axis */ + if(len == 3) { + time_0 = data_array[idx++]; + time_1 = (uint16_t)(data_array[idx++] << 8); + time_2 = (uint32_t)(data_array[idx++] << 16); + accel->sensortime = (uint32_t)(time_2 | time_1 | time_0); + } else { + accel->sensortime = 0; + } + } else { + rslt = BMI160_E_COM_FAIL; + } + + return rslt; +} + +/*! + * @brief This API reads accel data along with sensor time if time is requested + * by user. Kindly refer the user guide(README.md) for more info. + */ +static int8_t + get_gyro_data(uint8_t len, struct bmi160_sensor_data* gyro, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t idx = 0; + uint8_t data_array[15] = {0}; + uint8_t time_0 = 0; + uint16_t time_1 = 0; + uint32_t time_2 = 0; + uint8_t lsb; + uint8_t msb; + int16_t msblsb; + + if(len == 0) { + /* read gyro data only */ + rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 6, dev); + if(rslt == BMI160_OK) { + /* Gyro Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->x = msblsb; /* Data in X axis */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->y = msblsb; /* Data in Y axis */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->z = msblsb; /* Data in Z axis */ + gyro->sensortime = 0; + } else { + rslt = BMI160_E_COM_FAIL; + } + } else { + /* read gyro sensor data along with time */ + rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 12 + len, dev); + if(rslt == BMI160_OK) { + /* Gyro Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->x = msblsb; /* gyro X axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->y = msblsb; /* gyro Y axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->z = msblsb; /* gyro Z axis data */ + idx = idx + 6; + time_0 = data_array[idx++]; + time_1 = (uint16_t)(data_array[idx++] << 8); + time_2 = (uint32_t)(data_array[idx++] << 16); + gyro->sensortime = (uint32_t)(time_2 | time_1 | time_0); + } else { + rslt = BMI160_E_COM_FAIL; + } + } + + return rslt; +} + +/*! + * @brief This API reads accel and gyro data along with sensor time + * if time is requested by user. + * Kindly refer the user guide(README.md) for more info. + */ +static int8_t get_accel_gyro_data( + uint8_t len, + struct bmi160_sensor_data* accel, + struct bmi160_sensor_data* gyro, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t idx = 0; + uint8_t data_array[15] = {0}; + uint8_t time_0 = 0; + uint16_t time_1 = 0; + uint32_t time_2 = 0; + uint8_t lsb; + uint8_t msb; + int16_t msblsb; + + /* read both accel and gyro sensor data + * along with time if requested */ + rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 12 + len, dev); + if(rslt == BMI160_OK) { + /* Gyro Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->x = msblsb; /* gyro X axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->y = msblsb; /* gyro Y axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + gyro->z = msblsb; /* gyro Z axis data */ + /* Accel Data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->x = (int16_t)msblsb; /* accel X axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->y = (int16_t)msblsb; /* accel Y axis data */ + lsb = data_array[idx++]; + msb = data_array[idx++]; + msblsb = (int16_t)((msb << 8) | lsb); + accel->z = (int16_t)msblsb; /* accel Z axis data */ + if(len == 3) { + time_0 = data_array[idx++]; + time_1 = (uint16_t)(data_array[idx++] << 8); + time_2 = (uint32_t)(data_array[idx++] << 16); + accel->sensortime = (uint32_t)(time_2 | time_1 | time_0); + gyro->sensortime = (uint32_t)(time_2 | time_1 | time_0); + } else { + accel->sensortime = 0; + gyro->sensortime = 0; + } + } else { + rslt = BMI160_E_COM_FAIL; + } + + return rslt; +} + +/*! + * @brief This API enables the any-motion interrupt for accel. + */ +static int8_t enable_accel_any_motion_int( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable any motion x, any motion y, any motion z + * in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(any_motion_int_cfg->anymotion_en == BMI160_ENABLE) { + temp = data & ~BMI160_ANY_MOTION_X_INT_EN_MASK; + + /* Adding Any_motion x axis */ + data = temp | (any_motion_int_cfg->anymotion_x & BMI160_ANY_MOTION_X_INT_EN_MASK); + temp = data & ~BMI160_ANY_MOTION_Y_INT_EN_MASK; + + /* Adding Any_motion y axis */ + data = temp | + ((any_motion_int_cfg->anymotion_y << 1) & BMI160_ANY_MOTION_Y_INT_EN_MASK); + temp = data & ~BMI160_ANY_MOTION_Z_INT_EN_MASK; + + /* Adding Any_motion z axis */ + data = temp | + ((any_motion_int_cfg->anymotion_z << 2) & BMI160_ANY_MOTION_Z_INT_EN_MASK); + + /* any-motion feature selected*/ + dev->any_sig_sel = BMI160_ANY_MOTION_ENABLED; + } else { + data = data & ~BMI160_ANY_MOTION_ALL_INT_EN_MASK; + + /* neither any-motion feature nor sig-motion selected */ + dev->any_sig_sel = BMI160_BOTH_ANY_SIG_MOTION_DISABLED; + } + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API disable the sig-motion interrupt. + */ +static int8_t disable_sig_motion_int(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Disabling Significant motion interrupt if enabled */ + rslt = bmi160_get_regs(BMI160_INT_MOTION_3_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = (data & BMI160_SIG_MOTION_SEL_MASK); + if(temp) { + temp = data & ~BMI160_SIG_MOTION_SEL_MASK; + data = temp; + + /* Write data to register */ + rslt = bmi160_set_regs(BMI160_INT_MOTION_3_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API is used to map/unmap the Any/Sig motion, Step det/Low-g, + * Double tap, Single tap, Orientation, Flat, High-G, Nomotion interrupt pins. + */ +static int8_t + map_feature_interrupt(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data[3] = {0, 0, 0}; + uint8_t temp[3] = {0, 0, 0}; + + rslt = bmi160_get_regs(BMI160_INT_MAP_0_ADDR, data, 3, dev); + if(rslt == BMI160_OK) { + temp[0] = data[0] & ~int_mask_lookup_table[int_config->int_type]; + temp[2] = data[2] & ~int_mask_lookup_table[int_config->int_type]; + switch(int_config->int_channel) { + case BMI160_INT_CHANNEL_NONE: + data[0] = temp[0]; + data[2] = temp[2]; + break; + case BMI160_INT_CHANNEL_1: + data[0] = temp[0] | int_mask_lookup_table[int_config->int_type]; + data[2] = temp[2]; + break; + case BMI160_INT_CHANNEL_2: + data[2] = temp[2] | int_mask_lookup_table[int_config->int_type]; + data[0] = temp[0]; + break; + case BMI160_INT_CHANNEL_BOTH: + data[0] = temp[0] | int_mask_lookup_table[int_config->int_type]; + data[2] = temp[2] | int_mask_lookup_table[int_config->int_type]; + break; + default: + rslt = BMI160_E_OUT_OF_RANGE; + } + if(rslt == BMI160_OK) { + rslt = bmi160_set_regs(BMI160_INT_MAP_0_ADDR, data, 3, dev); + } + } + + return rslt; +} + +/*! + * @brief This API is used to map/unmap the Dataready(Accel & Gyro), FIFO full + * and FIFO watermark interrupt. + */ +static int8_t map_hardware_interrupt( + const struct bmi160_int_settg* int_config, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + rslt = bmi160_get_regs(BMI160_INT_MAP_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~int_mask_lookup_table[int_config->int_type]; + temp = temp & ~((uint8_t)(int_mask_lookup_table[int_config->int_type] << 4)); + switch(int_config->int_channel) { + case BMI160_INT_CHANNEL_NONE: + data = temp; + break; + case BMI160_INT_CHANNEL_1: + data = temp | (uint8_t)((int_mask_lookup_table[int_config->int_type]) << 4); + break; + case BMI160_INT_CHANNEL_2: + data = temp | int_mask_lookup_table[int_config->int_type]; + break; + case BMI160_INT_CHANNEL_BOTH: + data = temp | int_mask_lookup_table[int_config->int_type]; + data = data | (uint8_t)((int_mask_lookup_table[int_config->int_type]) << 4); + break; + default: + rslt = BMI160_E_OUT_OF_RANGE; + } + if(rslt == BMI160_OK) { + rslt = bmi160_set_regs(BMI160_INT_MAP_1_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for any-motion interrupt. + */ +static int8_t config_any_motion_src( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 1 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_MOTION_SRC_INT_MASK; + data = temp | ((any_motion_int_cfg->anymotion_data_src << 7) & BMI160_MOTION_SRC_INT_MASK); + + /* Write data to DATA 1 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the duration and threshold of + * any-motion interrupt. + */ +static int8_t config_any_dur_threshold( + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + uint8_t data_array[2] = {0}; + uint8_t dur; + + /* Configure Int Motion 0 register */ + rslt = bmi160_get_regs(BMI160_INT_MOTION_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* slope duration */ + dur = (uint8_t)any_motion_int_cfg->anymotion_dur; + temp = data & ~BMI160_SLOPE_INT_DUR_MASK; + data = temp | (dur & BMI160_MOTION_SRC_INT_MASK); + data_array[0] = data; + + /* add slope threshold */ + data_array[1] = any_motion_int_cfg->anymotion_thr; + + /* INT MOTION 0 and INT MOTION 1 address lie consecutively, + * hence writing data to respective registers at one go */ + + /* Writing to Int_motion 0 and + * Int_motion 1 Address simultaneously */ + rslt = bmi160_set_regs(BMI160_INT_MOTION_0_ADDR, data_array, 2, dev); + } + + return rslt; +} + +/*! + * @brief This API configure necessary setting of any-motion interrupt. + */ +static int8_t config_any_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_any_mot_int_cfg* any_motion_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = disable_sig_motion_int(dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_any_motion_src(any_motion_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_any_dur_threshold(any_motion_int_cfg, dev); + } + } + } + } + + return rslt; +} + +/*! + * @brief This API enable the data ready interrupt. + */ +static int8_t enable_data_ready_int(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable data ready interrupt in Int Enable 1 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_DATA_RDY_INT_EN_MASK; + data = temp | ((1 << 4) & BMI160_DATA_RDY_INT_EN_MASK); + + /* Writing data to INT ENABLE 1 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API enables the no motion/slow motion interrupt. + */ +static int8_t enable_no_motion_int( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable no motion x, no motion y, no motion z + * in Int Enable 2 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_2_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(no_mot_int_cfg->no_motion_x == 1) { + temp = data & ~BMI160_NO_MOTION_X_INT_EN_MASK; + + /* Adding No_motion x axis */ + data = temp | (1 & BMI160_NO_MOTION_X_INT_EN_MASK); + } + + if(no_mot_int_cfg->no_motion_y == 1) { + temp = data & ~BMI160_NO_MOTION_Y_INT_EN_MASK; + + /* Adding No_motion x axis */ + data = temp | ((1 << 1) & BMI160_NO_MOTION_Y_INT_EN_MASK); + } + + if(no_mot_int_cfg->no_motion_z == 1) { + temp = data & ~BMI160_NO_MOTION_Z_INT_EN_MASK; + + /* Adding No_motion x axis */ + data = temp | ((1 << 2) & BMI160_NO_MOTION_Z_INT_EN_MASK); + } + + /* write data to Int Enable 2 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_2_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the interrupt PIN setting for + * no motion/slow motion interrupt. + */ +static int8_t config_no_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_no_motion_data_src(no_mot_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_no_motion_dur_thr(no_mot_int_cfg, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API configure the source of interrupt for no motion. + */ +static int8_t config_no_motion_data_src( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 1 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_MOTION_SRC_INT_MASK; + data = temp | ((no_mot_int_cfg->no_motion_src << 7) & BMI160_MOTION_SRC_INT_MASK); + + /* Write data to DATA 1 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the duration and threshold of + * no motion/slow motion interrupt along with selection of no/slow motion. + */ +static int8_t config_no_motion_dur_thr( + const struct bmi160_acc_no_motion_int_cfg* no_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + uint8_t temp_1 = 0; + uint8_t reg_addr; + uint8_t data_array[2] = {0}; + + /* Configuring INT_MOTION register */ + reg_addr = BMI160_INT_MOTION_0_ADDR; + rslt = bmi160_get_regs(reg_addr, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_NO_MOTION_INT_DUR_MASK; + + /* Adding no_motion duration */ + data = temp | ((no_mot_int_cfg->no_motion_dur << 2) & BMI160_NO_MOTION_INT_DUR_MASK); + + /* Write data to NO_MOTION 0 address */ + rslt = bmi160_set_regs(reg_addr, &data, 1, dev); + if(rslt == BMI160_OK) { + reg_addr = BMI160_INT_MOTION_3_ADDR; + rslt = bmi160_get_regs(reg_addr, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_NO_MOTION_SEL_BIT_MASK; + + /* Adding no_motion_sel bit */ + temp_1 = (no_mot_int_cfg->no_motion_sel & BMI160_NO_MOTION_SEL_BIT_MASK); + data = (temp | temp_1); + data_array[1] = data; + + /* Adding no motion threshold */ + data_array[0] = no_mot_int_cfg->no_motion_thres; + reg_addr = BMI160_INT_MOTION_2_ADDR; + + /* writing data to INT_MOTION 2 and INT_MOTION 3 + * address simultaneously */ + rslt = bmi160_set_regs(reg_addr, data_array, 2, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API enables the sig-motion motion interrupt. + */ +static int8_t enable_sig_motion_int( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* For significant motion,enable any motion x,any motion y, + * any motion z in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(sig_mot_int_cfg->sig_en == BMI160_ENABLE) { + temp = data & ~BMI160_SIG_MOTION_INT_EN_MASK; + data = temp | (7 & BMI160_SIG_MOTION_INT_EN_MASK); + + /* sig-motion feature selected*/ + dev->any_sig_sel = BMI160_SIG_MOTION_ENABLED; + } else { + data = data & ~BMI160_SIG_MOTION_INT_EN_MASK; + + /* neither any-motion feature nor sig-motion selected */ + dev->any_sig_sel = BMI160_BOTH_ANY_SIG_MOTION_DISABLED; + } + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the interrupt PIN setting for + * significant motion interrupt. + */ +static int8_t config_sig_motion_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_sig_motion_data_src(sig_mot_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_sig_dur_threshold(sig_mot_int_cfg, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for sig motion interrupt. + */ +static int8_t config_sig_motion_data_src( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 1 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_MOTION_SRC_INT_MASK; + data = temp | ((sig_mot_int_cfg->sig_data_src << 7) & BMI160_MOTION_SRC_INT_MASK); + + /* Write data to DATA 1 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the threshold, skip and proof time of + * sig motion interrupt. + */ +static int8_t config_sig_dur_threshold( + const struct bmi160_acc_sig_mot_int_cfg* sig_mot_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data; + uint8_t temp = 0; + + /* Configuring INT_MOTION registers */ + + /* Write significant motion threshold. + * This threshold is same as any motion threshold */ + data = sig_mot_int_cfg->sig_mot_thres; + + /* Write data to INT_MOTION 1 address */ + rslt = bmi160_set_regs(BMI160_INT_MOTION_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + rslt = bmi160_get_regs(BMI160_INT_MOTION_3_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_SIG_MOTION_SKIP_MASK; + + /* adding skip time of sig_motion interrupt*/ + data = temp | ((sig_mot_int_cfg->sig_mot_skip << 2) & BMI160_SIG_MOTION_SKIP_MASK); + temp = data & ~BMI160_SIG_MOTION_PROOF_MASK; + + /* adding proof time of sig_motion interrupt */ + data = temp | ((sig_mot_int_cfg->sig_mot_proof << 4) & BMI160_SIG_MOTION_PROOF_MASK); + + /* configure the int_sig_mot_sel bit to select + * significant motion interrupt */ + temp = data & ~BMI160_SIG_MOTION_SEL_MASK; + data = temp | ((sig_mot_int_cfg->sig_en << 1) & BMI160_SIG_MOTION_SEL_MASK); + rslt = bmi160_set_regs(BMI160_INT_MOTION_3_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API enables the step detector interrupt. + */ +static int8_t enable_step_detect_int( + const struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable data ready interrupt in Int Enable 2 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_2_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_STEP_DETECT_INT_EN_MASK; + data = temp | + ((step_detect_int_cfg->step_detector_en << 3) & BMI160_STEP_DETECT_INT_EN_MASK); + + /* Writing data to INT ENABLE 2 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_2_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the step detector parameter. + */ +static int8_t config_step_detect( + const struct bmi160_acc_step_detect_int_cfg* step_detect_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data_array[2] = {0}; + + if(step_detect_int_cfg->step_detector_mode == BMI160_STEP_DETECT_NORMAL) { + /* Normal mode setting */ + data_array[0] = 0x15; + data_array[1] = 0x03; + } else if(step_detect_int_cfg->step_detector_mode == BMI160_STEP_DETECT_SENSITIVE) { + /* Sensitive mode setting */ + data_array[0] = 0x2D; + data_array[1] = 0x00; + } else if(step_detect_int_cfg->step_detector_mode == BMI160_STEP_DETECT_ROBUST) { + /* Robust mode setting */ + data_array[0] = 0x1D; + data_array[1] = 0x07; + } else if(step_detect_int_cfg->step_detector_mode == BMI160_STEP_DETECT_USER_DEFINE) { + /* Non recommended User defined setting */ + /* Configuring STEP_CONFIG register */ + rslt = bmi160_get_regs(BMI160_INT_STEP_CONFIG_0_ADDR, &data_array[0], 2, dev); + if(rslt == BMI160_OK) { + temp = data_array[0] & ~BMI160_STEP_DETECT_MIN_THRES_MASK; + + /* Adding min_threshold */ + data_array[0] = temp | ((step_detect_int_cfg->min_threshold << 3) & + BMI160_STEP_DETECT_MIN_THRES_MASK); + temp = data_array[0] & ~BMI160_STEP_DETECT_STEPTIME_MIN_MASK; + + /* Adding steptime_min */ + data_array[0] = temp | ((step_detect_int_cfg->steptime_min) & + BMI160_STEP_DETECT_STEPTIME_MIN_MASK); + temp = data_array[1] & ~BMI160_STEP_MIN_BUF_MASK; + + /* Adding steptime_min */ + data_array[1] = temp | + ((step_detect_int_cfg->step_min_buf) & BMI160_STEP_MIN_BUF_MASK); + } + } + + /* Write data to STEP_CONFIG register */ + rslt = bmi160_set_regs(BMI160_INT_STEP_CONFIG_0_ADDR, data_array, 2, dev); + + return rslt; +} + +/*! + * @brief This API enables the single/double tap interrupt. + */ +static int8_t enable_tap_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable single tap or double tap interrupt in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + if(int_config->int_type == BMI160_ACC_SINGLE_TAP_INT) { + temp = data & ~BMI160_SINGLE_TAP_INT_EN_MASK; + data = temp | ((tap_int_cfg->tap_en << 5) & BMI160_SINGLE_TAP_INT_EN_MASK); + } else { + temp = data & ~BMI160_DOUBLE_TAP_INT_EN_MASK; + data = temp | ((tap_int_cfg->tap_en << 4) & BMI160_DOUBLE_TAP_INT_EN_MASK); + } + + /* Write to Enable 0 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the interrupt PIN setting for + * tap interrupt. + */ +static int8_t config_tap_int_settg( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_feature_interrupt(int_config, dev); + if(rslt == BMI160_OK) { + rslt = config_tap_data_src(tap_int_cfg, dev); + if(rslt == BMI160_OK) { + rslt = config_tap_param(int_config, tap_int_cfg, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for tap interrupt. + */ +static int8_t config_tap_data_src( + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 0 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_TAP_SRC_INT_MASK; + data = temp | ((tap_int_cfg->tap_data_src << 3) & BMI160_TAP_SRC_INT_MASK); + + /* Write data to Data 0 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the parameters of tap interrupt. + * Threshold, quite, shock, and duration. + */ +static int8_t config_tap_param( + const struct bmi160_int_settg* int_config, + const struct bmi160_acc_tap_int_cfg* tap_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data = 0; + uint8_t data_array[2] = {0}; + uint8_t count = 0; + uint8_t dur, shock, quiet, thres; + + /* Configure tap 0 register for tap shock,tap quiet duration + * in case of single tap interrupt */ + rslt = bmi160_get_regs(BMI160_INT_TAP_0_ADDR, data_array, 2, dev); + if(rslt == BMI160_OK) { + data = data_array[count]; + if(int_config->int_type == BMI160_ACC_DOUBLE_TAP_INT) { + dur = (uint8_t)tap_int_cfg->tap_dur; + temp = (data & ~BMI160_TAP_DUR_MASK); + + /* Add tap duration data in case of + * double tap interrupt */ + data = temp | (dur & BMI160_TAP_DUR_MASK); + } + + shock = (uint8_t)tap_int_cfg->tap_shock; + temp = data & ~BMI160_TAP_SHOCK_DUR_MASK; + data = temp | ((shock << 6) & BMI160_TAP_SHOCK_DUR_MASK); + quiet = (uint8_t)tap_int_cfg->tap_quiet; + temp = data & ~BMI160_TAP_QUIET_DUR_MASK; + data = temp | ((quiet << 7) & BMI160_TAP_QUIET_DUR_MASK); + data_array[count++] = data; + data = data_array[count]; + thres = (uint8_t)tap_int_cfg->tap_thr; + temp = data & ~BMI160_TAP_THRES_MASK; + data = temp | (thres & BMI160_TAP_THRES_MASK); + data_array[count++] = data; + + /* TAP 0 and TAP 1 address lie consecutively, + * hence writing data to respective registers at one go */ + + /* Writing to Tap 0 and Tap 1 Address simultaneously */ + rslt = bmi160_set_regs(BMI160_INT_TAP_0_ADDR, data_array, count, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the secondary interface. + */ +static int8_t config_sec_if(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t if_conf = 0; + uint8_t cmd = BMI160_AUX_NORMAL_MODE; + + /* set the aux power mode to normal*/ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &cmd, 1, dev); + if(rslt == BMI160_OK) { + /* 0.5ms delay - refer datasheet table 24*/ + dev->delay_ms(1); + rslt = bmi160_get_regs(BMI160_IF_CONF_ADDR, &if_conf, 1, dev); + if_conf |= (uint8_t)(1 << 5); + if(rslt == BMI160_OK) { + /*enable the secondary interface also*/ + rslt = bmi160_set_regs(BMI160_IF_CONF_ADDR, &if_conf, 1, dev); + } + } + + return rslt; +} + +/*! + * @brief This API configure the ODR of the auxiliary sensor. + */ +static int8_t config_aux_odr(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t aux_odr; + + rslt = bmi160_get_regs(BMI160_AUX_ODR_ADDR, &aux_odr, 1, dev); + if(rslt == BMI160_OK) { + aux_odr = (uint8_t)(dev->aux_cfg.aux_odr); + + /* Set the secondary interface ODR + * i.e polling rate of secondary sensor */ + rslt = bmi160_set_regs(BMI160_AUX_ODR_ADDR, &aux_odr, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + } + + return rslt; +} + +/*! + * @brief This API maps the actual burst read length set by user. + */ +static int8_t map_read_len(uint16_t* len, const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + switch(dev->aux_cfg.aux_rd_burst_len) { + case BMI160_AUX_READ_LEN_0: + *len = 1; + break; + case BMI160_AUX_READ_LEN_1: + *len = 2; + break; + case BMI160_AUX_READ_LEN_2: + *len = 6; + break; + case BMI160_AUX_READ_LEN_3: + *len = 8; + break; + default: + rslt = BMI160_E_INVALID_INPUT; + break; + } + + return rslt; +} + +/*! + * @brief This API configure the settings of auxiliary sensor. + */ +static int8_t config_aux_settg(const struct bmi160_dev* dev) { + int8_t rslt; + + rslt = config_sec_if(dev); + if(rslt == BMI160_OK) { + /* Configures the auxiliary interface settings */ + rslt = bmi160_config_aux_mode(dev); + } + + return rslt; +} + +/*! + * @brief This API extract the read data from auxiliary sensor. + */ +static int8_t extract_aux_read( + uint16_t map_len, + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + uint8_t data[8] = { + 0, + }; + uint8_t read_addr = BMI160_AUX_DATA_ADDR; + uint8_t count = 0; + uint8_t read_count; + uint8_t read_len = (uint8_t)map_len; + + for(; count < len;) { + /* set address to read */ + rslt = bmi160_set_regs(BMI160_AUX_IF_2_ADDR, ®_addr, 1, dev); + dev->delay_ms(BMI160_AUX_COM_DELAY); + if(rslt == BMI160_OK) { + rslt = bmi160_get_regs(read_addr, data, map_len, dev); + if(rslt == BMI160_OK) { + read_count = 0; + + /* if read len is less the burst read len + * mention by user*/ + if(len < map_len) { + read_len = (uint8_t)len; + } else if((len - count) < map_len) { + read_len = (uint8_t)(len - count); + } + + for(; read_count < read_len; read_count++) { + aux_data[count + read_count] = data[read_count]; + } + + reg_addr += (uint8_t)map_len; + count += (uint8_t)map_len; + } else { + rslt = BMI160_E_COM_FAIL; + break; + } + } + } + + return rslt; +} + +/*! + * @brief This API enables the orient interrupt. + */ +static int8_t enable_orient_int( + const struct bmi160_acc_orient_int_cfg* orient_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable data ready interrupt in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_ORIENT_INT_EN_MASK; + data = temp | ((orient_int_cfg->orient_en << 6) & BMI160_ORIENT_INT_EN_MASK); + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the necessary setting of orientation interrupt. + */ +static int8_t config_orient_int_settg( + const struct bmi160_acc_orient_int_cfg* orient_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + uint8_t data_array[2] = {0, 0}; + + /* Configuring INT_ORIENT registers */ + rslt = bmi160_get_regs(BMI160_INT_ORIENT_0_ADDR, data_array, 2, dev); + if(rslt == BMI160_OK) { + data = data_array[0]; + temp = data & ~BMI160_ORIENT_MODE_MASK; + + /* Adding Orientation mode */ + data = temp | ((orient_int_cfg->orient_mode) & BMI160_ORIENT_MODE_MASK); + temp = data & ~BMI160_ORIENT_BLOCK_MASK; + + /* Adding Orientation blocking */ + data = temp | ((orient_int_cfg->orient_blocking << 2) & BMI160_ORIENT_BLOCK_MASK); + temp = data & ~BMI160_ORIENT_HYST_MASK; + + /* Adding Orientation hysteresis */ + data = temp | ((orient_int_cfg->orient_hyst << 4) & BMI160_ORIENT_HYST_MASK); + data_array[0] = data; + data = data_array[1]; + temp = data & ~BMI160_ORIENT_THETA_MASK; + + /* Adding Orientation threshold */ + data = temp | ((orient_int_cfg->orient_theta) & BMI160_ORIENT_THETA_MASK); + temp = data & ~BMI160_ORIENT_UD_ENABLE; + + /* Adding Orient_ud_en */ + data = temp | ((orient_int_cfg->orient_ud_en << 6) & BMI160_ORIENT_UD_ENABLE); + temp = data & ~BMI160_AXES_EN_MASK; + + /* Adding axes_en */ + data = temp | ((orient_int_cfg->axes_ex << 7) & BMI160_AXES_EN_MASK); + data_array[1] = data; + + /* Writing data to INT_ORIENT 0 and INT_ORIENT 1 + * registers simultaneously */ + rslt = bmi160_set_regs(BMI160_INT_ORIENT_0_ADDR, data_array, 2, dev); + } + + return rslt; +} + +/*! + * @brief This API enables the flat interrupt. + */ +static int8_t enable_flat_int( + const struct bmi160_acc_flat_detect_int_cfg* flat_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable flat interrupt in Int Enable 0 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_FLAT_INT_EN_MASK; + data = temp | ((flat_int->flat_en << 7) & BMI160_FLAT_INT_EN_MASK); + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the necessary setting of flat interrupt. + */ +static int8_t config_flat_int_settg( + const struct bmi160_acc_flat_detect_int_cfg* flat_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + uint8_t data_array[2] = {0, 0}; + + /* Configuring INT_FLAT register */ + rslt = bmi160_get_regs(BMI160_INT_FLAT_0_ADDR, data_array, 2, dev); + if(rslt == BMI160_OK) { + data = data_array[0]; + temp = data & ~BMI160_FLAT_THRES_MASK; + + /* Adding flat theta */ + data = temp | ((flat_int->flat_theta) & BMI160_FLAT_THRES_MASK); + data_array[0] = data; + data = data_array[1]; + temp = data & ~BMI160_FLAT_HOLD_TIME_MASK; + + /* Adding flat hold time */ + data = temp | ((flat_int->flat_hold_time << 4) & BMI160_FLAT_HOLD_TIME_MASK); + temp = data & ~BMI160_FLAT_HYST_MASK; + + /* Adding flat hysteresis */ + data = temp | ((flat_int->flat_hy) & BMI160_FLAT_HYST_MASK); + data_array[1] = data; + + /* Writing data to INT_FLAT 0 and INT_FLAT 1 + * registers simultaneously */ + rslt = bmi160_set_regs(BMI160_INT_FLAT_0_ADDR, data_array, 2, dev); + } + + return rslt; +} + +/*! + * @brief This API enables the Low-g interrupt. + */ +static int8_t enable_low_g_int( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable low-g interrupt in Int Enable 1 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_LOW_G_INT_EN_MASK; + data = temp | ((low_g_int->low_en << 3) & BMI160_LOW_G_INT_EN_MASK); + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for low-g interrupt. + */ +static int8_t config_low_g_data_src( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 0 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_LOW_HIGH_SRC_INT_MASK; + data = temp | ((low_g_int->low_data_src << 7) & BMI160_LOW_HIGH_SRC_INT_MASK); + + /* Write data to Data 0 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the necessary setting of low-g interrupt. + */ +static int8_t config_low_g_int_settg( + const struct bmi160_acc_low_g_int_cfg* low_g_int, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data_array[3] = {0, 0, 0}; + + /* Configuring INT_LOWHIGH register for low-g interrupt */ + rslt = bmi160_get_regs(BMI160_INT_LOWHIGH_2_ADDR, &data_array[2], 1, dev); + if(rslt == BMI160_OK) { + temp = data_array[2] & ~BMI160_LOW_G_HYST_MASK; + + /* Adding low-g hysteresis */ + data_array[2] = temp | (low_g_int->low_hyst & BMI160_LOW_G_HYST_MASK); + temp = data_array[2] & ~BMI160_LOW_G_LOW_MODE_MASK; + + /* Adding low-mode */ + data_array[2] = temp | ((low_g_int->low_mode << 2) & BMI160_LOW_G_LOW_MODE_MASK); + + /* Adding low-g threshold */ + data_array[1] = low_g_int->low_thres; + + /* Adding low-g interrupt delay */ + data_array[0] = low_g_int->low_dur; + + /* Writing data to INT_LOWHIGH 0,1,2 registers simultaneously*/ + rslt = bmi160_set_regs(BMI160_INT_LOWHIGH_0_ADDR, data_array, 3, dev); + } + + return rslt; +} + +/*! + * @brief This API enables the high-g interrupt. + */ +static int8_t enable_high_g_int( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Enable low-g interrupt in Int Enable 1 register */ + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Adding high-g X-axis */ + temp = data & ~BMI160_HIGH_G_X_INT_EN_MASK; + data = temp | (high_g_int_cfg->high_g_x & BMI160_HIGH_G_X_INT_EN_MASK); + + /* Adding high-g Y-axis */ + temp = data & ~BMI160_HIGH_G_Y_INT_EN_MASK; + data = temp | ((high_g_int_cfg->high_g_y << 1) & BMI160_HIGH_G_Y_INT_EN_MASK); + + /* Adding high-g Z-axis */ + temp = data & ~BMI160_HIGH_G_Z_INT_EN_MASK; + data = temp | ((high_g_int_cfg->high_g_z << 2) & BMI160_HIGH_G_Z_INT_EN_MASK); + + /* write data to Int Enable 0 register */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the source of data(filter & pre-filter) + * for high-g interrupt. + */ +static int8_t config_high_g_data_src( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + uint8_t temp = 0; + + /* Configure Int data 0 register to add source of interrupt */ + rslt = bmi160_get_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + temp = data & ~BMI160_LOW_HIGH_SRC_INT_MASK; + data = temp | ((high_g_int_cfg->high_data_src << 7) & BMI160_LOW_HIGH_SRC_INT_MASK); + + /* Write data to Data 0 address */ + rslt = bmi160_set_regs(BMI160_INT_DATA_0_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the necessary setting of high-g interrupt. + */ +static int8_t config_high_g_int_settg( + const struct bmi160_acc_high_g_int_cfg* high_g_int_cfg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data_array[3] = {0, 0, 0}; + + rslt = bmi160_get_regs(BMI160_INT_LOWHIGH_2_ADDR, &data_array[0], 1, dev); + if(rslt == BMI160_OK) { + temp = data_array[0] & ~BMI160_HIGH_G_HYST_MASK; + + /* Adding high-g hysteresis */ + data_array[0] = temp | ((high_g_int_cfg->high_hy << 6) & BMI160_HIGH_G_HYST_MASK); + + /* Adding high-g duration */ + data_array[1] = high_g_int_cfg->high_dur; + + /* Adding high-g threshold */ + data_array[2] = high_g_int_cfg->high_thres; + rslt = bmi160_set_regs(BMI160_INT_LOWHIGH_2_ADDR, data_array, 3, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the behavioural setting of interrupt pin. + */ +static int8_t + config_int_out_ctrl(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data = 0; + + /* Configuration of output interrupt signals on pins INT1 and INT2 are + * done in BMI160_INT_OUT_CTRL_ADDR register*/ + rslt = bmi160_get_regs(BMI160_INT_OUT_CTRL_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* updating the interrupt pin structure to local structure */ + const struct bmi160_int_pin_settg* intr_pin_sett = &(int_config->int_pin_settg); + + /* Configuring channel 1 */ + if(int_config->int_channel == BMI160_INT_CHANNEL_1) { + /* Output enable */ + temp = data & ~BMI160_INT1_OUTPUT_EN_MASK; + data = temp | ((intr_pin_sett->output_en << 3) & BMI160_INT1_OUTPUT_EN_MASK); + + /* Output mode */ + temp = data & ~BMI160_INT1_OUTPUT_MODE_MASK; + data = temp | ((intr_pin_sett->output_mode << 2) & BMI160_INT1_OUTPUT_MODE_MASK); + + /* Output type */ + temp = data & ~BMI160_INT1_OUTPUT_TYPE_MASK; + data = temp | ((intr_pin_sett->output_type << 1) & BMI160_INT1_OUTPUT_TYPE_MASK); + + /* edge control */ + temp = data & ~BMI160_INT1_EDGE_CTRL_MASK; + data = temp | ((intr_pin_sett->edge_ctrl) & BMI160_INT1_EDGE_CTRL_MASK); + } else { + /* Configuring channel 2 */ + /* Output enable */ + temp = data & ~BMI160_INT2_OUTPUT_EN_MASK; + data = temp | ((intr_pin_sett->output_en << 7) & BMI160_INT2_OUTPUT_EN_MASK); + + /* Output mode */ + temp = data & ~BMI160_INT2_OUTPUT_MODE_MASK; + data = temp | ((intr_pin_sett->output_mode << 6) & BMI160_INT2_OUTPUT_MODE_MASK); + + /* Output type */ + temp = data & ~BMI160_INT2_OUTPUT_TYPE_MASK; + data = temp | ((intr_pin_sett->output_type << 5) & BMI160_INT2_OUTPUT_TYPE_MASK); + + /* edge control */ + temp = data & ~BMI160_INT2_EDGE_CTRL_MASK; + data = temp | ((intr_pin_sett->edge_ctrl << 4) & BMI160_INT2_EDGE_CTRL_MASK); + } + + rslt = bmi160_set_regs(BMI160_INT_OUT_CTRL_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API configure the mode(input enable, latch or non-latch) of interrupt pin. + */ +static int8_t + config_int_latch(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t temp = 0; + uint8_t data = 0; + + /* Configuration of latch on pins INT1 and INT2 are done in + * BMI160_INT_LATCH_ADDR register*/ + rslt = bmi160_get_regs(BMI160_INT_LATCH_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* updating the interrupt pin structure to local structure */ + const struct bmi160_int_pin_settg* intr_pin_sett = &(int_config->int_pin_settg); + if(int_config->int_channel == BMI160_INT_CHANNEL_1) { + /* Configuring channel 1 */ + /* Input enable */ + temp = data & ~BMI160_INT1_INPUT_EN_MASK; + data = temp | ((intr_pin_sett->input_en << 4) & BMI160_INT1_INPUT_EN_MASK); + } else { + /* Configuring channel 2 */ + /* Input enable */ + temp = data & ~BMI160_INT2_INPUT_EN_MASK; + data = temp | ((intr_pin_sett->input_en << 5) & BMI160_INT2_INPUT_EN_MASK); + } + + /* In case of latch interrupt,update the latch duration */ + + /* Latching holds the interrupt for the amount of latch + * duration time */ + temp = data & ~BMI160_INT_LATCH_MASK; + data = temp | (intr_pin_sett->latch_dur & BMI160_INT_LATCH_MASK); + + /* OUT_CTRL_INT and LATCH_INT address lie consecutively, + * hence writing data to respective registers at one go */ + rslt = bmi160_set_regs(BMI160_INT_LATCH_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API performs the self test for accelerometer of BMI160 + */ +static int8_t perform_accel_self_test(struct bmi160_dev* dev) { + int8_t rslt; + struct bmi160_sensor_data accel_pos, accel_neg; + + /* Enable Gyro self test bit */ + rslt = enable_accel_self_test(dev); + if(rslt == BMI160_OK) { + /* Perform accel self test with positive excitation */ + rslt = accel_self_test_positive_excitation(&accel_pos, dev); + if(rslt == BMI160_OK) { + /* Perform accel self test with negative excitation */ + rslt = accel_self_test_negative_excitation(&accel_neg, dev); + if(rslt == BMI160_OK) { + /* Validate the self test result */ + rslt = validate_accel_self_test(&accel_pos, &accel_neg); + } + } + } + + return rslt; +} + +/*! + * @brief This API enables to perform the accel self test by setting proper + * configurations to facilitate accel self test + */ +static int8_t enable_accel_self_test(struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Set the Accel power mode as normal mode */ + dev->accel_cfg.power = BMI160_ACCEL_NORMAL_MODE; + + /* Set the sensor range configuration as 8G */ + dev->accel_cfg.range = BMI160_ACCEL_RANGE_8G; + rslt = bmi160_set_sens_conf(dev); + if(rslt == BMI160_OK) { + /* Accel configurations are set to facilitate self test + * acc_odr - 1600Hz ; acc_bwp = 2 ; acc_us = 0 */ + reg_data = BMI160_ACCEL_SELF_TEST_CONFIG; + rslt = bmi160_set_regs(BMI160_ACCEL_CONFIG_ADDR, ®_data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API performs accel self test with positive excitation + */ +static int8_t accel_self_test_positive_excitation( + struct bmi160_sensor_data* accel_pos, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Enable accel self test with positive self-test excitation + * and with amplitude of deflection set as high */ + reg_data = BMI160_ACCEL_SELF_TEST_POSITIVE_EN; + rslt = bmi160_set_regs(BMI160_SELF_TEST_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + /* Read the data after a delay of 50ms - refer datasheet 2.8.1 accel self test*/ + dev->delay_ms(BMI160_ACCEL_SELF_TEST_DELAY); + rslt = bmi160_get_sensor_data(BMI160_ACCEL_ONLY, accel_pos, NULL, dev); + } + + return rslt; +} + +/*! + * @brief This API performs accel self test with negative excitation + */ +static int8_t accel_self_test_negative_excitation( + struct bmi160_sensor_data* accel_neg, + const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Enable accel self test with negative self-test excitation + * and with amplitude of deflection set as high */ + reg_data = BMI160_ACCEL_SELF_TEST_NEGATIVE_EN; + rslt = bmi160_set_regs(BMI160_SELF_TEST_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + /* Read the data after a delay of 50ms */ + dev->delay_ms(BMI160_ACCEL_SELF_TEST_DELAY); + rslt = bmi160_get_sensor_data(BMI160_ACCEL_ONLY, accel_neg, NULL, dev); + } + + return rslt; +} + +/*! + * @brief This API validates the accel self test results + */ +static int8_t validate_accel_self_test( + const struct bmi160_sensor_data* accel_pos, + const struct bmi160_sensor_data* accel_neg) { + int8_t rslt; + + /* Validate the results of self test */ + if(((accel_neg->x - accel_pos->x) > BMI160_ACCEL_SELF_TEST_LIMIT) && + ((accel_neg->y - accel_pos->y) > BMI160_ACCEL_SELF_TEST_LIMIT) && + ((accel_neg->z - accel_pos->z) > BMI160_ACCEL_SELF_TEST_LIMIT)) { + /* Self test pass condition */ + rslt = BMI160_OK; + } else { + rslt = BMI160_W_ACCEl_SELF_TEST_FAIL; + } + + return rslt; +} + +/*! + * @brief This API performs the self test for gyroscope of BMI160 + */ +static int8_t perform_gyro_self_test(const struct bmi160_dev* dev) { + int8_t rslt; + + /* Enable Gyro self test bit */ + rslt = enable_gyro_self_test(dev); + if(rslt == BMI160_OK) { + /* Validate the gyro self test a delay of 50ms */ + dev->delay_ms(50); + + /* Validate the gyro self test results */ + rslt = validate_gyro_self_test(dev); + } + + return rslt; +} + +/*! + * @brief This API enables the self test bit to trigger self test for Gyro + */ +static int8_t enable_gyro_self_test(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Enable the Gyro self test bit to trigger the self test */ + rslt = bmi160_get_regs(BMI160_SELF_TEST_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + reg_data = BMI160_SET_BITS(reg_data, BMI160_GYRO_SELF_TEST, 1); + rslt = bmi160_set_regs(BMI160_SELF_TEST_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + /* Delay to enable gyro self test */ + dev->delay_ms(15); + } + } + + return rslt; +} + +/*! + * @brief This API validates the self test results of Gyro + */ +static int8_t validate_gyro_self_test(const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t reg_data; + + /* Validate the Gyro self test result */ + rslt = bmi160_get_regs(BMI160_STATUS_ADDR, ®_data, 1, dev); + if(rslt == BMI160_OK) { + reg_data = BMI160_GET_BITS(reg_data, BMI160_GYRO_SELF_TEST_STATUS); + if(reg_data == BMI160_ENABLE) { + /* Gyro self test success case */ + rslt = BMI160_OK; + } else { + rslt = BMI160_W_GYRO_SELF_TEST_FAIL; + } + } + + return rslt; +} + +/*! + * @brief This API sets FIFO full interrupt of the sensor.This interrupt + * occurs when the FIFO is full and the next full data sample would cause + * a FIFO overflow, which may delete the old samples. + */ +static int8_t + set_fifo_full_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + /* Null-pointer check */ + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /*enable the fifo full interrupt */ + rslt = enable_fifo_full_int(int_config, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_hardware_interrupt(int_config, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This enable the FIFO full interrupt engine. + */ +static int8_t + enable_fifo_full_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + data = BMI160_SET_BITS(data, BMI160_FIFO_FULL_INT, int_config->fifo_full_int_en); + + /* Writing data to INT ENABLE 1 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API sets FIFO watermark interrupt of the sensor.The FIFO + * watermark interrupt is fired, when the FIFO fill level is above a fifo + * watermark. + */ +static int8_t set_fifo_watermark_int( + const struct bmi160_int_settg* int_config, + const struct bmi160_dev* dev) { + int8_t rslt = BMI160_OK; + + if((dev == NULL) || (dev->delay_ms == NULL)) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Enable fifo-watermark interrupt in Int Enable 1 register */ + rslt = enable_fifo_wtm_int(int_config, dev); + if(rslt == BMI160_OK) { + /* Configure Interrupt pins */ + rslt = set_intr_pin_config(int_config, dev); + if(rslt == BMI160_OK) { + rslt = map_hardware_interrupt(int_config, dev); + } + } + } + + return rslt; +} + +/*! + * @brief This enable the FIFO watermark interrupt engine. + */ +static int8_t + enable_fifo_wtm_int(const struct bmi160_int_settg* int_config, const struct bmi160_dev* dev) { + int8_t rslt; + uint8_t data = 0; + + rslt = bmi160_get_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + data = BMI160_SET_BITS(data, BMI160_FIFO_WTM_INT, int_config->fifo_wtm_int_en); + + /* Writing data to INT ENABLE 1 Address */ + rslt = bmi160_set_regs(BMI160_INT_ENABLE_1_ADDR, &data, 1, dev); + } + + return rslt; +} + +/*! + * @brief This API is used to reset the FIFO related configurations + * in the fifo_frame structure. + */ +static void reset_fifo_data_structure(const struct bmi160_dev* dev) { + /*Prepare for next FIFO read by resetting FIFO's + * internal data structures*/ + dev->fifo->accel_byte_start_idx = 0; + dev->fifo->gyro_byte_start_idx = 0; + dev->fifo->aux_byte_start_idx = 0; + dev->fifo->sensor_time = 0; + dev->fifo->skipped_frame_count = 0; +} + +/*! + * @brief This API is used to read fifo_byte_counter value (i.e) + * current fill-level in Fifo buffer. + */ +static int8_t get_fifo_byte_counter(uint16_t* bytes_to_read, struct bmi160_dev const* dev) { + int8_t rslt = 0; + uint8_t data[2]; + uint8_t addr = BMI160_FIFO_LENGTH_ADDR; + + rslt |= bmi160_get_regs(addr, data, 2, dev); + data[1] = data[1] & BMI160_FIFO_BYTE_COUNTER_MASK; + + /* Available data in FIFO is stored in bytes_to_read*/ + *bytes_to_read = (((uint16_t)data[1] << 8) | ((uint16_t)data[0])); + + return rslt; +} + +/*! + * @brief This API is used to compute the number of bytes of accel FIFO data + * which is to be parsed in header-less mode + */ +static void get_accel_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* acc_frame_count, + const struct bmi160_dev* dev) { + /* Data start index */ + *data_index = dev->fifo->accel_byte_start_idx; + if(dev->fifo->fifo_data_enable == BMI160_FIFO_A_ENABLE) { + *data_read_length = (*acc_frame_count) * BMI160_FIFO_A_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_G_A_ENABLE) { + *data_read_length = (*acc_frame_count) * BMI160_FIFO_GA_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_A_ENABLE) { + *data_read_length = (*acc_frame_count) * BMI160_FIFO_MA_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_A_ENABLE) { + *data_read_length = (*acc_frame_count) * BMI160_FIFO_MGA_LENGTH; + } else { + /* When accel is not enabled ,there will be no accel data. + * so we update the data index as complete */ + *data_index = dev->fifo->length; + } + + if(*data_read_length > dev->fifo->length) { + /* Handling the case where more data is requested + * than that is available*/ + *data_read_length = dev->fifo->length; + } +} + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed. + */ +static void unpack_accel_frame( + struct bmi160_sensor_data* acc, + uint16_t* idx, + uint8_t* acc_idx, + uint8_t frame_info, + const struct bmi160_dev* dev) { + switch(frame_info) { + case BMI160_FIFO_HEAD_A: + case BMI160_FIFO_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_A_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into the structure instance "acc" */ + unpack_accel_data(&acc[*acc_idx], *idx, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_A_LENGTH; + (*acc_idx)++; + break; + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_GA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "acc"*/ + unpack_accel_data(&acc[*acc_idx], *idx + BMI160_FIFO_G_LENGTH, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_GA_LENGTH; + (*acc_idx)++; + break; + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_M_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "acc"*/ + unpack_accel_data(&acc[*acc_idx], *idx + BMI160_FIFO_M_LENGTH, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_MA_LENGTH; + (*acc_idx)++; + break; + case BMI160_FIFO_HEAD_M_G_A: + case BMI160_FIFO_M_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MGA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "acc"*/ + unpack_accel_data(&acc[*acc_idx], *idx + BMI160_FIFO_MG_LENGTH, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_MGA_LENGTH; + (*acc_idx)++; + break; + case BMI160_FIFO_HEAD_M: + case BMI160_FIFO_M_ENABLE: + (*idx) = (*idx) + BMI160_FIFO_M_LENGTH; + break; + case BMI160_FIFO_HEAD_G: + case BMI160_FIFO_G_ENABLE: + (*idx) = (*idx) + BMI160_FIFO_G_LENGTH; + break; + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_M_G_ENABLE: + (*idx) = (*idx) + BMI160_FIFO_MG_LENGTH; + break; + default: + break; + } +} + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data and store it in the instance of the structure bmi160_sensor_data. + */ +static void unpack_accel_data( + struct bmi160_sensor_data* accel_data, + uint16_t data_start_index, + const struct bmi160_dev* dev) { + uint16_t data_lsb; + uint16_t data_msb; + + /* Accel raw x data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + accel_data->x = (int16_t)((data_msb << 8) | data_lsb); + + /* Accel raw y data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + accel_data->y = (int16_t)((data_msb << 8) | data_lsb); + + /* Accel raw z data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + accel_data->z = (int16_t)((data_msb << 8) | data_lsb); +} + +/*! + * @brief This API is used to parse the accelerometer data from the + * FIFO data in header mode. + */ +static void extract_accel_header_mode( + struct bmi160_sensor_data* accel_data, + uint8_t* accel_length, + const struct bmi160_dev* dev) { + uint8_t frame_header = 0; + uint16_t data_index; + uint8_t accel_index = 0; + + for(data_index = dev->fifo->accel_byte_start_idx; data_index < dev->fifo->length;) { + /* extracting Frame header */ + frame_header = (dev->fifo->data[data_index] & BMI160_FIFO_TAG_INTR_MASK); + + /*Index is moved to next byte where the data is starting*/ + data_index++; + switch(frame_header) { + /* Accel frame */ + case BMI160_FIFO_HEAD_A: + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_HEAD_M_G_A: + unpack_accel_frame(accel_data, &data_index, &accel_index, frame_header, dev); + break; + case BMI160_FIFO_HEAD_M: + move_next_frame(&data_index, BMI160_FIFO_M_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_G: + move_next_frame(&data_index, BMI160_FIFO_G_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_M_G: + move_next_frame(&data_index, BMI160_FIFO_MG_LENGTH, dev); + break; + + /* Sensor time frame */ + case BMI160_FIFO_HEAD_SENSOR_TIME: + unpack_sensortime_frame(&data_index, dev); + break; + + /* Skip frame */ + case BMI160_FIFO_HEAD_SKIP_FRAME: + unpack_skipped_frame(&data_index, dev); + break; + + /* Input config frame */ + case BMI160_FIFO_HEAD_INPUT_CONFIG: + move_next_frame(&data_index, 1, dev); + break; + case BMI160_FIFO_HEAD_OVER_READ: + + /* Update the data index as complete in case of Over read */ + data_index = dev->fifo->length; + break; + default: + break; + } + if(*accel_length == accel_index) { + /* Number of frames to read completed */ + break; + } + } + + /*Update number of accel data read*/ + *accel_length = accel_index; + + /*Update the accel frame index*/ + dev->fifo->accel_byte_start_idx = data_index; +} + +/*! + * @brief This API computes the number of bytes of gyro FIFO data + * which is to be parsed in header-less mode + */ +static void get_gyro_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* gyro_frame_count, + const struct bmi160_dev* dev) { + /* Data start index */ + *data_index = dev->fifo->gyro_byte_start_idx; + if(dev->fifo->fifo_data_enable == BMI160_FIFO_G_ENABLE) { + *data_read_length = (*gyro_frame_count) * BMI160_FIFO_G_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_G_A_ENABLE) { + *data_read_length = (*gyro_frame_count) * BMI160_FIFO_GA_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_ENABLE) { + *data_read_length = (*gyro_frame_count) * BMI160_FIFO_MG_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_A_ENABLE) { + *data_read_length = (*gyro_frame_count) * BMI160_FIFO_MGA_LENGTH; + } else { + /* When gyro is not enabled ,there will be no gyro data. + * so we update the data index as complete */ + *data_index = dev->fifo->length; + } + + if(*data_read_length > dev->fifo->length) { + /* Handling the case where more data is requested + * than that is available*/ + *data_read_length = dev->fifo->length; + } +} + +/*! + * @brief This API is used to parse the gyroscope's data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed. + */ +static void unpack_gyro_frame( + struct bmi160_sensor_data* gyro, + uint16_t* idx, + uint8_t* gyro_idx, + uint8_t frame_info, + const struct bmi160_dev* dev) { + switch(frame_info) { + case BMI160_FIFO_HEAD_G: + case BMI160_FIFO_G_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_G_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "gyro"*/ + unpack_gyro_data(&gyro[*gyro_idx], *idx, dev); + + /*Move the data index*/ + (*idx) = (*idx) + BMI160_FIFO_G_LENGTH; + (*gyro_idx)++; + break; + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_GA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance "gyro" */ + unpack_gyro_data(&gyro[*gyro_idx], *idx, dev); + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_GA_LENGTH; + (*gyro_idx)++; + break; + case BMI160_FIFO_HEAD_M_G_A: + case BMI160_FIFO_M_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MGA_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "gyro"*/ + unpack_gyro_data(&gyro[*gyro_idx], *idx + BMI160_FIFO_M_LENGTH, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_MGA_LENGTH; + (*gyro_idx)++; + break; + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_M_A_ENABLE: + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_MA_LENGTH; + break; + case BMI160_FIFO_HEAD_M: + case BMI160_FIFO_M_ENABLE: + (*idx) = (*idx) + BMI160_FIFO_M_LENGTH; + break; + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_M_G_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MG_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *idx = dev->fifo->length; + break; + } + + /*Unpack the data array into structure instance "gyro"*/ + unpack_gyro_data(&gyro[*gyro_idx], *idx + BMI160_FIFO_M_LENGTH, dev); + + /*Move the data index*/ + (*idx) = (*idx) + BMI160_FIFO_MG_LENGTH; + (*gyro_idx)++; + break; + case BMI160_FIFO_HEAD_A: + case BMI160_FIFO_A_ENABLE: + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_A_LENGTH; + break; + default: + break; + } +} + +/*! + * @brief This API is used to parse the gyro data from the + * FIFO data and store it in the instance of the structure bmi160_sensor_data. + */ +static void unpack_gyro_data( + struct bmi160_sensor_data* gyro_data, + uint16_t data_start_index, + const struct bmi160_dev* dev) { + uint16_t data_lsb; + uint16_t data_msb; + + /* Gyro raw x data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + gyro_data->x = (int16_t)((data_msb << 8) | data_lsb); + + /* Gyro raw y data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + gyro_data->y = (int16_t)((data_msb << 8) | data_lsb); + + /* Gyro raw z data */ + data_lsb = dev->fifo->data[data_start_index++]; + data_msb = dev->fifo->data[data_start_index++]; + gyro_data->z = (int16_t)((data_msb << 8) | data_lsb); +} + +/*! + * @brief This API is used to parse the gyro data from the + * FIFO data in header mode. + */ +static void extract_gyro_header_mode( + struct bmi160_sensor_data* gyro_data, + uint8_t* gyro_length, + const struct bmi160_dev* dev) { + uint8_t frame_header = 0; + uint16_t data_index; + uint8_t gyro_index = 0; + + for(data_index = dev->fifo->gyro_byte_start_idx; data_index < dev->fifo->length;) { + /* extracting Frame header */ + frame_header = (dev->fifo->data[data_index] & BMI160_FIFO_TAG_INTR_MASK); + + /*Index is moved to next byte where the data is starting*/ + data_index++; + switch(frame_header) { + /* GYRO frame */ + case BMI160_FIFO_HEAD_G: + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_HEAD_M_G_A: + unpack_gyro_frame(gyro_data, &data_index, &gyro_index, frame_header, dev); + break; + case BMI160_FIFO_HEAD_A: + move_next_frame(&data_index, BMI160_FIFO_A_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_M: + move_next_frame(&data_index, BMI160_FIFO_M_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_M_A: + move_next_frame(&data_index, BMI160_FIFO_M_LENGTH, dev); + break; + + /* Sensor time frame */ + case BMI160_FIFO_HEAD_SENSOR_TIME: + unpack_sensortime_frame(&data_index, dev); + break; + + /* Skip frame */ + case BMI160_FIFO_HEAD_SKIP_FRAME: + unpack_skipped_frame(&data_index, dev); + break; + + /* Input config frame */ + case BMI160_FIFO_HEAD_INPUT_CONFIG: + move_next_frame(&data_index, 1, dev); + break; + case BMI160_FIFO_HEAD_OVER_READ: + + /* Update the data index as complete in case of over read */ + data_index = dev->fifo->length; + break; + default: + break; + } + if(*gyro_length == gyro_index) { + /*Number of frames to read completed*/ + break; + } + } + + /*Update number of gyro data read*/ + *gyro_length = gyro_index; + + /*Update the gyro frame index*/ + dev->fifo->gyro_byte_start_idx = data_index; +} + +/*! + * @brief This API computes the number of bytes of aux FIFO data + * which is to be parsed in header-less mode + */ +static void get_aux_len_to_parse( + uint16_t* data_index, + uint16_t* data_read_length, + const uint8_t* aux_frame_count, + const struct bmi160_dev* dev) { + /* Data start index */ + *data_index = dev->fifo->gyro_byte_start_idx; + if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_ENABLE) { + *data_read_length = (*aux_frame_count) * BMI160_FIFO_M_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_A_ENABLE) { + *data_read_length = (*aux_frame_count) * BMI160_FIFO_MA_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_ENABLE) { + *data_read_length = (*aux_frame_count) * BMI160_FIFO_MG_LENGTH; + } else if(dev->fifo->fifo_data_enable == BMI160_FIFO_M_G_A_ENABLE) { + *data_read_length = (*aux_frame_count) * BMI160_FIFO_MGA_LENGTH; + } else { + /* When aux is not enabled ,there will be no aux data. + * so we update the data index as complete */ + *data_index = dev->fifo->length; + } + + if(*data_read_length > dev->fifo->length) { + /* Handling the case where more data is requested + * than that is available */ + *data_read_length = dev->fifo->length; + } +} + +/*! + * @brief This API is used to parse the aux's data from the + * FIFO data in both header mode and header-less mode. + * It updates the idx value which is used to store the index of + * the current data byte which is parsed + */ +static void unpack_aux_frame( + struct bmi160_aux_data* aux_data, + uint16_t* idx, + uint8_t* aux_index, + uint8_t frame_info, + const struct bmi160_dev* dev) { + switch(frame_info) { + case BMI160_FIFO_HEAD_M: + case BMI160_FIFO_M_ENABLE: + + /* Partial read, then skip the data */ + if((*idx + BMI160_FIFO_M_LENGTH) > dev->fifo->length) { + /* Update the data index as complete */ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance */ + unpack_aux_data(&aux_data[*aux_index], *idx, dev); + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_M_LENGTH; + (*aux_index)++; + break; + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_M_A_ENABLE: + + /* Partial read, then skip the data */ + if((*idx + BMI160_FIFO_MA_LENGTH) > dev->fifo->length) { + /* Update the data index as complete */ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance */ + unpack_aux_data(&aux_data[*aux_index], *idx, dev); + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_MA_LENGTH; + (*aux_index)++; + break; + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_M_G_ENABLE: + + /* Partial read, then skip the data */ + if((*idx + BMI160_FIFO_MG_LENGTH) > dev->fifo->length) { + /* Update the data index as complete */ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance */ + unpack_aux_data(&aux_data[*aux_index], *idx, dev); + + /* Move the data index */ + (*idx) = (*idx) + BMI160_FIFO_MG_LENGTH; + (*aux_index)++; + break; + case BMI160_FIFO_HEAD_M_G_A: + case BMI160_FIFO_M_G_A_ENABLE: + + /*Partial read, then skip the data*/ + if((*idx + BMI160_FIFO_MGA_LENGTH) > dev->fifo->length) { + /* Update the data index as complete */ + *idx = dev->fifo->length; + break; + } + + /* Unpack the data array into structure instance */ + unpack_aux_data(&aux_data[*aux_index], *idx, dev); + + /*Move the data index*/ + *idx = *idx + BMI160_FIFO_MGA_LENGTH; + (*aux_index)++; + break; + case BMI160_FIFO_HEAD_G: + case BMI160_FIFO_G_ENABLE: + + /* Move the data index */ + (*idx) = (*idx) + BMI160_FIFO_G_LENGTH; + break; + case BMI160_FIFO_HEAD_G_A: + case BMI160_FIFO_G_A_ENABLE: + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_GA_LENGTH; + break; + case BMI160_FIFO_HEAD_A: + case BMI160_FIFO_A_ENABLE: + + /* Move the data index */ + *idx = *idx + BMI160_FIFO_A_LENGTH; + break; + default: + break; + } +} + +/*! + * @brief This API is used to parse the aux data from the + * FIFO data and store it in the instance of the structure bmi160_aux_data. + */ +static void unpack_aux_data( + struct bmi160_aux_data* aux_data, + uint16_t data_start_index, + const struct bmi160_dev* dev) { + /* Aux data bytes */ + aux_data->data[0] = dev->fifo->data[data_start_index++]; + aux_data->data[1] = dev->fifo->data[data_start_index++]; + aux_data->data[2] = dev->fifo->data[data_start_index++]; + aux_data->data[3] = dev->fifo->data[data_start_index++]; + aux_data->data[4] = dev->fifo->data[data_start_index++]; + aux_data->data[5] = dev->fifo->data[data_start_index++]; + aux_data->data[6] = dev->fifo->data[data_start_index++]; + aux_data->data[7] = dev->fifo->data[data_start_index++]; +} + +/*! + * @brief This API is used to parse the aux data from the + * FIFO data in header mode. + */ +static void extract_aux_header_mode( + struct bmi160_aux_data* aux_data, + uint8_t* aux_length, + const struct bmi160_dev* dev) { + uint8_t frame_header = 0; + uint16_t data_index; + uint8_t aux_index = 0; + + for(data_index = dev->fifo->aux_byte_start_idx; data_index < dev->fifo->length;) { + /* extracting Frame header */ + frame_header = (dev->fifo->data[data_index] & BMI160_FIFO_TAG_INTR_MASK); + + /*Index is moved to next byte where the data is starting*/ + data_index++; + switch(frame_header) { + /* Aux frame */ + case BMI160_FIFO_HEAD_M: + case BMI160_FIFO_HEAD_M_A: + case BMI160_FIFO_HEAD_M_G: + case BMI160_FIFO_HEAD_M_G_A: + unpack_aux_frame(aux_data, &data_index, &aux_index, frame_header, dev); + break; + case BMI160_FIFO_HEAD_G: + move_next_frame(&data_index, BMI160_FIFO_G_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_G_A: + move_next_frame(&data_index, BMI160_FIFO_GA_LENGTH, dev); + break; + case BMI160_FIFO_HEAD_A: + move_next_frame(&data_index, BMI160_FIFO_A_LENGTH, dev); + break; + + /* Sensor time frame */ + case BMI160_FIFO_HEAD_SENSOR_TIME: + unpack_sensortime_frame(&data_index, dev); + break; + + /* Skip frame */ + case BMI160_FIFO_HEAD_SKIP_FRAME: + unpack_skipped_frame(&data_index, dev); + break; + + /* Input config frame */ + case BMI160_FIFO_HEAD_INPUT_CONFIG: + move_next_frame(&data_index, 1, dev); + break; + case BMI160_FIFO_HEAD_OVER_READ: + + /* Update the data index as complete in case + * of over read */ + data_index = dev->fifo->length; + break; + default: + + /* Update the data index as complete in case of + * getting other headers like 0x00 */ + data_index = dev->fifo->length; + break; + } + if(*aux_length == aux_index) { + /*Number of frames to read completed*/ + break; + } + } + + /* Update number of aux data read */ + *aux_length = aux_index; + + /* Update the aux frame index */ + dev->fifo->aux_byte_start_idx = data_index; +} + +/*! + * @brief This API checks the presence of non-valid frames in the read fifo data. + */ +static void check_frame_validity(uint16_t* data_index, const struct bmi160_dev* dev) { + if((*data_index + 2) < dev->fifo->length) { + /* Check if FIFO is empty */ + if((dev->fifo->data[*data_index] == FIFO_CONFIG_MSB_CHECK) && + (dev->fifo->data[*data_index + 1] == FIFO_CONFIG_LSB_CHECK)) { + /*Update the data index as complete*/ + *data_index = dev->fifo->length; + } + } +} + +/*! + * @brief This API is used to move the data index ahead of the + * current_frame_length parameter when unnecessary FIFO data appears while + * extracting the user specified data. + */ +static void move_next_frame( + uint16_t* data_index, + uint8_t current_frame_length, + const struct bmi160_dev* dev) { + /*Partial read, then move the data index to last data*/ + if((*data_index + current_frame_length) > dev->fifo->length) { + /*Update the data index as complete*/ + *data_index = dev->fifo->length; + } else { + /*Move the data index to next frame*/ + *data_index = *data_index + current_frame_length; + } +} + +/*! + * @brief This API is used to parse and store the sensor time from the + * FIFO data in the structure instance dev. + */ +static void unpack_sensortime_frame(uint16_t* data_index, const struct bmi160_dev* dev) { + uint32_t sensor_time_byte3 = 0; + uint16_t sensor_time_byte2 = 0; + uint8_t sensor_time_byte1 = 0; + + /*Partial read, then move the data index to last data*/ + if((*data_index + BMI160_SENSOR_TIME_LENGTH) > dev->fifo->length) { + /*Update the data index as complete*/ + *data_index = dev->fifo->length; + } else { + sensor_time_byte3 = dev->fifo->data[(*data_index) + BMI160_SENSOR_TIME_MSB_BYTE] << 16; + sensor_time_byte2 = dev->fifo->data[(*data_index) + BMI160_SENSOR_TIME_XLSB_BYTE] << 8; + sensor_time_byte1 = dev->fifo->data[(*data_index)]; + + /* Sensor time */ + dev->fifo->sensor_time = + (uint32_t)(sensor_time_byte3 | sensor_time_byte2 | sensor_time_byte1); + *data_index = (*data_index) + BMI160_SENSOR_TIME_LENGTH; + } +} + +/*! + * @brief This API is used to parse and store the skipped_frame_count from + * the FIFO data in the structure instance dev. + */ +static void unpack_skipped_frame(uint16_t* data_index, const struct bmi160_dev* dev) { + /*Partial read, then move the data index to last data*/ + if(*data_index >= dev->fifo->length) { + /*Update the data index as complete*/ + *data_index = dev->fifo->length; + } else { + dev->fifo->skipped_frame_count = dev->fifo->data[*data_index]; + + /*Move the data index*/ + *data_index = (*data_index) + 1; + } +} + +/*! + * @brief This API is used to get the FOC status from the sensor + */ +static int8_t get_foc_status(uint8_t* foc_status, struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data; + + /* Read the FOC status from sensor */ + rslt = bmi160_get_regs(BMI160_STATUS_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Get the foc_status bit */ + *foc_status = BMI160_GET_BITS(data, BMI160_FOC_STATUS); + } + + return rslt; +} + +/*! + * @brief This API is used to configure the offset enable bits in the sensor + */ +static int8_t + configure_offset_enable(const struct bmi160_foc_conf* foc_conf, struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t data; + + /* Null-pointer check */ + rslt = null_ptr_check(dev); + if(rslt != BMI160_OK) { + rslt = BMI160_E_NULL_PTR; + } else { + /* Read the FOC config from the sensor */ + rslt = bmi160_get_regs(BMI160_OFFSET_CONF_ADDR, &data, 1, dev); + if(rslt == BMI160_OK) { + /* Set the offset enable/disable for gyro */ + data = BMI160_SET_BITS(data, BMI160_GYRO_OFFSET_EN, foc_conf->gyro_off_en); + + /* Set the offset enable/disable for accel */ + data = BMI160_SET_BITS(data, BMI160_ACCEL_OFFSET_EN, foc_conf->acc_off_en); + + /* Set the offset config in the sensor */ + rslt = bmi160_set_regs(BMI160_OFFSET_CONF_ADDR, &data, 1, dev); + } + } + + return rslt; +} + +static int8_t trigger_foc(struct bmi160_offsets* offset, struct bmi160_dev const* dev) { + int8_t rslt; + uint8_t foc_status = BMI160_ENABLE; + uint8_t cmd = BMI160_START_FOC_CMD; + uint8_t timeout = 0; + uint8_t data_array[20]; + + /* Start the FOC process */ + rslt = bmi160_set_regs(BMI160_COMMAND_REG_ADDR, &cmd, 1, dev); + if(rslt == BMI160_OK) { + /* Check the FOC status*/ + rslt = get_foc_status(&foc_status, dev); + + if((rslt != BMI160_OK) || (foc_status != BMI160_ENABLE)) { + while((foc_status != BMI160_ENABLE) && (timeout < 11)) { + /* Maximum time of 250ms is given in 10 + * steps of 25ms each - 250ms refer datasheet 2.9.1 */ + dev->delay_ms(25); + + /* Check the FOC status*/ + rslt = get_foc_status(&foc_status, dev); + timeout++; + } + + if((rslt == BMI160_OK) && (foc_status == BMI160_ENABLE)) { + /* Get offset values from sensor */ + rslt = bmi160_get_offsets(offset, dev); + } else { + /* FOC failure case */ + rslt = BMI160_E_FOC_FAILURE; + } + } + + if(rslt == BMI160_OK) { + /* Read registers 0x04-0x17 */ + rslt = bmi160_get_regs(BMI160_GYRO_DATA_ADDR, data_array, 20, dev); + } + } + + return rslt; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/bmi160.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/bmi160.h new file mode 100644 index 000000000..d4d98094c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/bmi160.h @@ -0,0 +1,992 @@ +/** +* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved. +* +* BSD-3-Clause +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* @file bmi160.h +* @date 2021-10-05 +* @version v3.9.2 +* +*/ + +/*! + * @defgroup bmi160 BMI160 + */ + +#ifndef BMI160_H_ +#define BMI160_H_ + +/*************************** C++ guard macro *****************************/ +#ifdef __cplusplus +extern "C" { +#endif + +#include "bmi160_defs.h" +#ifdef __KERNEL__ +#include +#else +#include +#include +#include +#endif + +/*********************** User function prototypes ************************/ + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiInit Initialization + * @brief Initialize the sensor and device structure + */ + +/*! + * \ingroup bmi160ApiInit + * \page bmi160_api_bmi160_init bmi160_init + * \code + * int8_t bmi160_init(struct bmi160_dev *dev); + * \endcode + * @details This API is the entry point for sensor.It performs + * the selection of I2C/SPI read mechanism according to the + * selected interface and reads the chip-id of bmi160 sensor. + * + * @param[in,out] dev : Structure instance of bmi160_dev + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_init(struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiRegs Registers + * @brief Read data from the given register address of sensor + */ + +/*! + * \ingroup bmi160ApiRegs + * \page bmi160_api_bmi160_get_regs bmi160_get_regs + * \code + * int8_t bmi160_get_regs(uint8_t reg_addr, uint8_t *data, uint16_t len, const struct bmi160_dev *dev); + * \endcode + * @details This API reads the data from the given register address of sensor. + * + * @param[in] reg_addr : Register address from where the data to be read + * @param[out] data : Pointer to data buffer to store the read data. + * @param[in] len : No of bytes of data to be read. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note For most of the registers auto address increment applies, with the + * exception of a few special registers, which trap the address. For e.g., + * Register address - 0x24(BMI160_FIFO_DATA_ADDR) + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t + bmi160_get_regs(uint8_t reg_addr, uint8_t* data, uint16_t len, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiRegs + * \page bmi160_api_bmi160_set_regs bmi160_set_regs + * \code + * int8_t bmi160_set_regs(uint8_t reg_addr, uint8_t *data, uint16_t len, const struct bmi160_dev *dev); + * \endcode + * @details This API writes the given data to the register address + * of sensor. + * + * @param[in] reg_addr : Register address from where the data to be written. + * @param[in] data : Pointer to data buffer which is to be written + * in the sensor. + * @param[in] len : No of bytes of data to write.. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t + bmi160_set_regs(uint8_t reg_addr, uint8_t* data, uint16_t len, const struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiSoftreset Soft reset + * @brief Perform soft reset of the sensor + */ + +/*! + * \ingroup bmi160ApiSoftreset + * \page bmi160_api_bmi160_soft_reset bmi160_soft_reset + * \code + * int8_t bmi160_soft_reset(struct bmi160_dev *dev); + * \endcode + * @details This API resets and restarts the device. + * All register values are overwritten with default parameters. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_soft_reset(struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiConfig Configuration + * @brief Configuration of the sensor + */ + +/*! + * \ingroup bmi160ApiConfig + * \page bmi160_api_bmi160_set_sens_conf bmi160_set_sens_conf + * \code + * int8_t bmi160_set_sens_conf(struct bmi160_dev *dev); + * \endcode + * @details This API configures the power mode, range and bandwidth + * of sensor. + * + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_sens_conf(struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiConfig + * \page bmi160_api_bmi160_get_sens_conf bmi160_get_sens_conf + * \code + * int8_t bmi160_get_sens_conf(struct bmi160_dev *dev); + * \endcode + * @details This API gets accel and gyro configurations. + * + * @param[out] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_get_sens_conf(struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiPowermode Power mode + * @brief Set / Get power mode of the sensor + */ + +/*! + * \ingroup bmi160ApiPowermode + * \page bmi160_api_bmi160_set_power_mode bmi160_set_power_mode + * \code + * int8_t bmi160_set_power_mode(struct bmi160_dev *dev); + * \endcode + * @details This API sets the power mode of the sensor. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_power_mode(struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiPowermode + * \page bmi160_api_bmi160_get_power_mode bmi160_get_power_mode + * \code + * int8_t bmi160_get_power_mode(struct bmi160_dev *dev); + * \endcode + * @details This API gets the power mode of the sensor. + * + * @param[in] dev : Structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_get_power_mode(struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiData Sensor Data + * @brief Read sensor data + */ + +/*! + * \ingroup bmi160ApiData + * \page bmi160_api_bmi160_get_sensor_data bmi160_get_sensor_data + * \code + * int8_t bmi160_get_sensor_data(uint8_t select_sensor, + * struct bmi160_sensor_data *accel, + * struct bmi160_sensor_data *gyro, + * const struct bmi160_dev *dev); + * + * \endcode + * @details This API reads sensor data, stores it in + * the bmi160_sensor_data structure pointer passed by the user. + * The user can ask for accel data ,gyro data or both sensor + * data using bmi160_select_sensor enum + * + * @param[in] select_sensor : enum to choose accel,gyro or both sensor data + * @param[out] accel : Structure pointer to store accel data + * @param[out] gyro : Structure pointer to store gyro data + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_get_sensor_data( + uint8_t select_sensor, + struct bmi160_sensor_data* accel, + struct bmi160_sensor_data* gyro, + const struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiInt Interrupt configuration + * @brief Set interrupt configuration of the sensor + */ + +/*! + * \ingroup bmi160ApiInt + * \page bmi160_api_bmi160_set_int_config bmi160_set_int_config + * \code + * int8_t bmi160_set_int_config(struct bmi160_int_settg *int_config, struct bmi160_dev *dev); + * \endcode + * @details This API configures the necessary interrupt based on + * the user settings in the bmi160_int_settg structure instance. + * + * @param[in] int_config : Structure instance of bmi160_int_settg. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_int_config(struct bmi160_int_settg* int_config, struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiStepC Step counter + * @brief Step counter operations + */ + +/*! + * \ingroup bmi160ApiStepC + * \page bmi160_api_bmi160_set_step_counter bmi160_set_step_counter + * \code + * int8_t bmi160_set_step_counter(uint8_t step_cnt_enable, const struct bmi160_dev *dev); + * \endcode + * @details This API enables the step counter feature. + * + * @param[in] step_cnt_enable : value to enable or disable + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_step_counter(uint8_t step_cnt_enable, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiStepC + * \page bmi160_api_bmi160_read_step_counter bmi160_read_step_counter + * \code + * int8_t bmi160_read_step_counter(uint16_t *step_val, const struct bmi160_dev *dev); + * \endcode + * @details This API reads the step counter value. + * + * @param[in] step_val : Pointer to store the step counter value. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_read_step_counter(uint16_t* step_val, const struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiAux Auxiliary sensor + * @brief Auxiliary sensor operations + */ + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_aux_read bmi160_aux_read + * \code + * int8_t bmi160_aux_read(uint8_t reg_addr, uint8_t *aux_data, uint16_t len, const struct bmi160_dev *dev); + * \endcode + * @details This API reads the mention no of byte of data from the given + * register address of auxiliary sensor. + * + * @param[in] reg_addr : Address of register to read. + * @param[in] aux_data : Pointer to store the read data. + * @param[in] len : No of bytes to read. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_aux_read( + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_aux_write bmi160_aux_write + * \code + * int8_t bmi160_aux_write(uint8_t reg_addr, uint8_t *aux_data, uint16_t len, const struct bmi160_dev *dev); + * \endcode + * @details This API writes the mention no of byte of data to the given + * register address of auxiliary sensor. + * + * @param[in] reg_addr : Address of register to write. + * @param[in] aux_data : Pointer to write data. + * @param[in] len : No of bytes to write. + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_aux_write( + uint8_t reg_addr, + uint8_t* aux_data, + uint16_t len, + const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_aux_init bmi160_aux_init + * \code + * int8_t bmi160_aux_init(const struct bmi160_dev *dev); + * \endcode + * @details This API initialize the auxiliary sensor + * in order to access it. + * + * @param[in] dev : Structure instance of bmi160_dev. + * @note : Refer user guide for detailed info. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_aux_init(const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_set_aux_auto_mode bmi160_set_aux_auto_mode + * \code + * int8_t bmi160_set_aux_auto_mode(uint8_t *data_addr, struct bmi160_dev *dev); + * \endcode + * @details This API is used to setup the auxiliary sensor of bmi160 in auto mode + * Thus enabling the auto update of 8 bytes of data from auxiliary sensor + * to BMI160 register address 0x04 to 0x0B + * + * @param[in] data_addr : Starting address of aux. sensor's data register + * (BMI160 registers 0x04 to 0x0B will be updated + * with 8 bytes of data from auxiliary sensor + * starting from this register address.) + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note : Set the value of auxiliary polling rate by setting + * dev->aux_cfg.aux_odr to the required value from the table + * before calling this API + * + *@verbatim + * dev->aux_cfg.aux_odr | Auxiliary ODR (Hz) + * -----------------------|----------------------- + * BMI160_AUX_ODR_0_78HZ | 25/32 + * BMI160_AUX_ODR_1_56HZ | 25/16 + * BMI160_AUX_ODR_3_12HZ | 25/8 + * BMI160_AUX_ODR_6_25HZ | 25/4 + * BMI160_AUX_ODR_12_5HZ | 25/2 + * BMI160_AUX_ODR_25HZ | 25 + * BMI160_AUX_ODR_50HZ | 50 + * BMI160_AUX_ODR_100HZ | 100 + * BMI160_AUX_ODR_200HZ | 200 + * BMI160_AUX_ODR_400HZ | 400 + * BMI160_AUX_ODR_800HZ | 800 + *@endverbatim + * + * @note : Other values of dev->aux_cfg.aux_odr are reserved and not for use + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_set_aux_auto_mode(uint8_t* data_addr, struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_config_aux_mode bmi160_config_aux_mode + * \code + * int8_t bmi160_config_aux_mode(const struct bmi160_dev *dev); + * \endcode + * @details This API configures the 0x4C register and settings like + * Auxiliary sensor manual enable/ disable and aux burst read length. + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_config_aux_mode(const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiAux + * \page bmi160_api_bmi160_read_aux_data_auto_mode bmi160_read_aux_data_auto_mode + * \code + * int8_t bmi160_read_aux_data_auto_mode(uint8_t *aux_data, const struct bmi160_dev *dev); + * \endcode + * @details This API is used to read the raw uncompensated auxiliary sensor + * data of 8 bytes from BMI160 register address 0x04 to 0x0B + * + * @param[in] aux_data : Pointer to user array of length 8 bytes + * Ensure that the aux_data array is of + * length 8 bytes + * @param[in] dev : Structure instance of bmi160_dev + * + * @retval zero -> Success / -ve value -> Error + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_read_aux_data_auto_mode(uint8_t* aux_data, const struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiSelfTest Self test + * @brief Perform self test of the sensor + */ + +/*! + * \ingroup bmi160ApiSelfTest + * \page bmi160_api_bmi160_perform_self_test bmi160_perform_self_test + * \code + * int8_t bmi160_perform_self_test(uint8_t select_sensor, struct bmi160_dev *dev); + * \endcode + * @details This is used to perform self test of accel/gyro of the BMI160 sensor + * + * @param[in] select_sensor : enum to choose accel or gyro for self test + * @param[in] dev : Structure instance of bmi160_dev + * + * @note self test can be performed either for accel/gyro at any instant. + * + *@verbatim + * value of select_sensor | Inference + *----------------------------------|-------------------------------- + * BMI160_ACCEL_ONLY | Accel self test enabled + * BMI160_GYRO_ONLY | Gyro self test enabled + * BMI160_BOTH_ACCEL_AND_GYRO | NOT TO BE USED + *@endverbatim + * + * @note The return value of this API gives us the result of self test. + * + * @note Performing self test does soft reset of the sensor, User can + * set the desired settings after performing the self test. + * + * @return Result of API execution status + * @retval BMI160_OK Self test success + * @retval BMI160_W_GYRO_SELF_TEST_FAIL Gyro self test fail + * @retval BMI160_W_ACCEl_SELF_TEST_FAIL Accel self test fail + */ +int8_t bmi160_perform_self_test(uint8_t select_sensor, struct bmi160_dev* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiFIFO FIFO + * @brief FIFO operations of the sensor + */ + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_get_fifo_data bmi160_get_fifo_data + * \code + * int8_t bmi160_get_fifo_data(struct bmi160_dev const *dev); + * \endcode + * @details This API reads data from the fifo buffer. + * + * @note User has to allocate the FIFO buffer along with + * corresponding fifo length from his side before calling this API + * as mentioned in the readme.md + * + * @note User must specify the number of bytes to read from the FIFO in + * dev->fifo->length , It will be updated by the number of bytes actually + * read from FIFO after calling this API + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval Zero Success + * @retval Negative Error + */ +int8_t bmi160_get_fifo_data(struct bmi160_dev const* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_set_fifo_flush bmi160_set_fifo_flush + * \code + * int8_t bmi160_set_fifo_flush(const struct bmi160_dev *dev); + * \endcode + * @details This API writes fifo_flush command to command register.This + * action clears all data in the Fifo without changing fifo configuration + * settings. + * + * @param[in] dev : Structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_set_fifo_flush(const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_set_fifo_config bmi160_set_fifo_config + * \code + * int8_t bmi160_set_fifo_config(uint8_t config, uint8_t enable, struct bmi160_dev const *dev); + * \endcode + * @details This API sets the FIFO configuration in the sensor. + * + * @param[in] config : variable used to specify the FIFO + * configurations which are to be enabled or disabled in the sensor. + * + * @note : User can set either set one or more or all FIFO configurations + * by ORing the below mentioned macros. + * + *@verbatim + * config | Value + * ------------------------|--------------------------- + * BMI160_FIFO_TIME | 0x02 + * BMI160_FIFO_TAG_INT2 | 0x04 + * BMI160_FIFO_TAG_INT1 | 0x08 + * BMI160_FIFO_HEADER | 0x10 + * BMI160_FIFO_AUX | 0x20 + * BMI160_FIFO_ACCEL | 0x40 + * BMI160_FIFO_GYRO | 0x80 + *@endverbatim + * + * @param[in] enable : Parameter used to enable or disable the above + * FIFO configuration + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return status of bus communication result + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_set_fifo_config(uint8_t config, uint8_t enable, struct bmi160_dev const* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_set_fifo_down bmi160_set_fifo_down + * \code + * int8_t bmi160_set_fifo_down(uint8_t fifo_down, const struct bmi160_dev *dev); + * \endcode + * @details This API is used to configure the down sampling ratios of + * the accel and gyro data for FIFO.Also, it configures filtered or + * pre-filtered data for the fifo for accel and gyro. + * + * @param[in] fifo_down : variable used to specify the FIFO down + * configurations which are to be enabled or disabled in the sensor. + * + * @note The user must select one among the following macros to + * select down-sampling ratio for accel + * + *@verbatim + * config | Value + * -------------------------------------|--------------------------- + * BMI160_ACCEL_FIFO_DOWN_ZERO | 0x00 + * BMI160_ACCEL_FIFO_DOWN_ONE | 0x10 + * BMI160_ACCEL_FIFO_DOWN_TWO | 0x20 + * BMI160_ACCEL_FIFO_DOWN_THREE | 0x30 + * BMI160_ACCEL_FIFO_DOWN_FOUR | 0x40 + * BMI160_ACCEL_FIFO_DOWN_FIVE | 0x50 + * BMI160_ACCEL_FIFO_DOWN_SIX | 0x60 + * BMI160_ACCEL_FIFO_DOWN_SEVEN | 0x70 + *@endverbatim + * + * @note The user must select one among the following macros to + * select down-sampling ratio for gyro + * + *@verbatim + * config | Value + * -------------------------------------|--------------------------- + * BMI160_GYRO_FIFO_DOWN_ZERO | 0x00 + * BMI160_GYRO_FIFO_DOWN_ONE | 0x01 + * BMI160_GYRO_FIFO_DOWN_TWO | 0x02 + * BMI160_GYRO_FIFO_DOWN_THREE | 0x03 + * BMI160_GYRO_FIFO_DOWN_FOUR | 0x04 + * BMI160_GYRO_FIFO_DOWN_FIVE | 0x05 + * BMI160_GYRO_FIFO_DOWN_SIX | 0x06 + * BMI160_GYRO_FIFO_DOWN_SEVEN | 0x07 + *@endverbatim + * + * @note The user can enable filtered accel data by the following macro + * + *@verbatim + * config | Value + * -------------------------------------|--------------------------- + * BMI160_ACCEL_FIFO_FILT_EN | 0x80 + *@endverbatim + * + * @note The user can enable filtered gyro data by the following macro + * + *@verbatim + * config | Value + * -------------------------------------|--------------------------- + * BMI160_GYRO_FIFO_FILT_EN | 0x08 + *@endverbatim + * + * @note : By ORing the above mentioned macros, the user can select + * the required FIFO down config settings + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return status of bus communication result + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_set_fifo_down(uint8_t fifo_down, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_set_fifo_wm bmi160_set_fifo_wm + * \code + * int8_t bmi160_set_fifo_wm(uint8_t fifo_wm, const struct bmi160_dev *dev); + * \endcode + * @details This API sets the FIFO watermark level in the sensor. + * + * @note The FIFO watermark is issued when the FIFO fill level is + * equal or above the watermark level and units of watermark is 4 bytes. + * + * @param[in] fifo_wm : Variable used to set the FIFO water mark level + * @param[in] dev : Structure instance of bmi160_dev + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_set_fifo_wm(uint8_t fifo_wm, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_extract_accel bmi160_extract_accel + * \code + * int8_t bmi160_extract_accel(struct bmi160_sensor_data *accel_data, uint8_t *accel_length, struct bmi160_dev const + **dev); + * \endcode + * @details This API parses and extracts the accelerometer frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "accel_data" structure instance. + * + * @note The bmi160_extract_accel API should be called only after + * reading the FIFO data by calling the bmi160_get_fifo_data() API. + * + * @param[out] accel_data : Structure instance of bmi160_sensor_data + * where the accelerometer data in FIFO is stored. + * @param[in,out] accel_length : Number of valid accelerometer frames + * (x,y,z axes data) read out from fifo. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note accel_length is updated with the number of valid accelerometer + * frames extracted from fifo (1 accel frame = 6 bytes) at the end of + * execution of this API. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_extract_accel( + struct bmi160_sensor_data* accel_data, + uint8_t* accel_length, + struct bmi160_dev const* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_extract_gyro bmi160_extract_gyro + * \code + * int8_t bmi160_extract_gyro(struct bmi160_sensor_data *gyro_data, uint8_t *gyro_length, struct bmi160_dev const *dev); + * \endcode + * @details This API parses and extracts the gyro frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the "gyro_data" structure instance. + * + * @note The bmi160_extract_gyro API should be called only after + * reading the FIFO data by calling the bmi160_get_fifo_data() API. + * + * @param[out] gyro_data : Structure instance of bmi160_sensor_data + * where the gyro data in FIFO is stored. + * @param[in,out] gyro_length : Number of valid gyro frames + * (x,y,z axes data) read out from fifo. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note gyro_length is updated with the number of valid gyro + * frames extracted from fifo (1 gyro frame = 6 bytes) at the end of + * execution of this API. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_extract_gyro( + struct bmi160_sensor_data* gyro_data, + uint8_t* gyro_length, + struct bmi160_dev const* dev); + +/*! + * \ingroup bmi160ApiFIFO + * \page bmi160_api_bmi160_extract_aux bmi160_extract_aux + * \code + * int8_t bmi160_extract_aux(struct bmi160_aux_data *aux_data, uint8_t *aux_len, struct bmi160_dev const *dev); + * \endcode + * @details This API parses and extracts the aux frames from + * FIFO data read by the "bmi160_get_fifo_data" API and stores it in + * the bmi160_aux_data structure instance. + * + * @note The bmi160_extract_aux API should be called only after + * reading the FIFO data by calling the bmi160_get_fifo_data() API. + * + * @param[out] aux_data : Structure instance of bmi160_aux_data + * where the aux data in FIFO is stored. + * @param[in,out] aux_len : Number of valid aux frames (8bytes) + * read out from FIFO. + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note aux_len is updated with the number of valid aux + * frames extracted from fifo (1 aux frame = 8 bytes) at the end of + * execution of this API. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + * + */ +int8_t bmi160_extract_aux( + struct bmi160_aux_data* aux_data, + uint8_t* aux_len, + struct bmi160_dev const* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiFOC FOC + * @brief Start FOC of accel and gyro sensors + */ + +/*! + * \ingroup bmi160ApiFOC + * \page bmi160_api_bmi160_start_foc bmi160_start_foc + * \code + * int8_t bmi160_start_foc(const struct bmi160_foc_conf *foc_conf, + * \endcode + * @details This API starts the FOC of accel and gyro + * + * @note FOC should not be used in low-power mode of sensor + * + * @note Accel FOC targets values of +1g , 0g , -1g + * Gyro FOC always targets value of 0 dps + * + * @param[in] foc_conf : Structure instance of bmi160_foc_conf which + * has the FOC configuration + * @param[in,out] offset : Structure instance to store Offset + * values read from sensor + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note Pre-requisites for triggering FOC in accel , Set the following, + * Enable the acc_off_en + * Ex : foc_conf.acc_off_en = BMI160_ENABLE; + * + * Set the desired target values of FOC to each axes (x,y,z) by using the + * following macros + * - BMI160_FOC_ACCEL_DISABLED + * - BMI160_FOC_ACCEL_POSITIVE_G + * - BMI160_FOC_ACCEL_NEGATIVE_G + * - BMI160_FOC_ACCEL_0G + * + * Ex : foc_conf.foc_acc_x = BMI160_FOC_ACCEL_0G; + * foc_conf.foc_acc_y = BMI160_FOC_ACCEL_0G; + * foc_conf.foc_acc_z = BMI160_FOC_ACCEL_POSITIVE_G; + * + * @note Pre-requisites for triggering FOC in gyro , + * Set the following parameters, + * + * Ex : foc_conf.foc_gyr_en = BMI160_ENABLE; + * foc_conf.gyro_off_en = BMI160_ENABLE; + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_start_foc( + const struct bmi160_foc_conf* foc_conf, + struct bmi160_offsets* offset, + struct bmi160_dev const* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiOffsets Offsets + * @brief Set / Get offset values of accel and gyro sensors + */ + +/*! + * \ingroup bmi160ApiOffsets + * \page bmi160_api_bmi160_get_offsets bmi160_get_offsets + * \code + * int8_t bmi160_get_offsets(struct bmi160_offsets *offset, const struct bmi160_dev *dev); + * \endcode + * @details This API reads and stores the offset values of accel and gyro + * + * @param[in,out] offset : Structure instance of bmi160_offsets in which + * the offset values are read and stored + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_get_offsets(struct bmi160_offsets* offset, const struct bmi160_dev* dev); + +/*! + * \ingroup bmi160ApiOffsets + * \page bmi160_api_bmi160_set_offsets bmi160_set_offsets + * \code + * int8_t bmi160_set_offsets(const struct bmi160_foc_conf *foc_conf, + * const struct bmi160_offsets *offset, + * struct bmi160_dev const *dev); + * \endcode + * @details This API writes the offset values of accel and gyro to + * the sensor but these values will be reset on POR or soft reset. + * + * @param[in] foc_conf : Structure instance of bmi160_foc_conf which + * has the FOC configuration + * @param[in] offset : Structure instance in which user updates offset + * values which are to be written in the sensor + * @param[in] dev : Structure instance of bmi160_dev. + * + * @note Offsets can be set by user like offset->off_acc_x = 10; + * where 1LSB = 3.9mg and for gyro 1LSB = 0.061degrees/second + * + * @note BMI160 offset values for xyz axes of accel should be within range of + * BMI160_ACCEL_MIN_OFFSET (-128) to BMI160_ACCEL_MAX_OFFSET (127) + * + * @note BMI160 offset values for xyz axes of gyro should be within range of + * BMI160_GYRO_MIN_OFFSET (-512) to BMI160_GYRO_MAX_OFFSET (511) + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_set_offsets( + const struct bmi160_foc_conf* foc_conf, + const struct bmi160_offsets* offset, + struct bmi160_dev const* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiNVM NVM + * @brief Write image registers values to NVM + */ + +/*! + * \ingroup bmi160ApiNVM + * \page bmi160_api_bmi160_update_nvm bmi160_update_nvm + * \code + * int8_t bmi160_update_nvm(struct bmi160_dev const *dev); + * \endcode + * @details This API writes the image registers values to NVM which is + * stored even after POR or soft reset + * + * @param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_update_nvm(struct bmi160_dev const* dev); + +/** + * \ingroup bmi160 + * \defgroup bmi160ApiInts Interrupt status + * @brief Read interrupt status from the sensor + */ + +/*! + * \ingroup bmi160ApiInts + * \page bmi160_api_bmi160_get_int_status bmi160_get_int_status + * \code + * int8_t bmi160_get_int_status(enum bmi160_int_status_sel int_status_sel, + * union bmi160_int_status *int_status, + * struct bmi160_dev const *dev); + * \endcode + * @details This API gets the interrupt status from the sensor. + * + * @param[in] int_status_sel : Enum variable to select either individual or all the + * interrupt status bits. + * @param[in] int_status : pointer variable to get the interrupt status + * from the sensor. + * param[in] dev : Structure instance of bmi160_dev. + * + * @return Result of API execution status + * @retval 0 -> Success + * @retval Any non zero value -> Fail + */ +int8_t bmi160_get_int_status( + enum bmi160_int_status_sel int_status_sel, + union bmi160_int_status* int_status, + struct bmi160_dev const* dev); + +/*************************** C++ guard macro *****************************/ +#ifdef __cplusplus +} +#endif + +#endif /* BMI160_H_ */ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/bmi160_defs.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/bmi160_defs.h new file mode 100644 index 000000000..458ecaad5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/bmi160_defs.h @@ -0,0 +1,1619 @@ +/** +* Copyright (c) 2021 Bosch Sensortec GmbH. All rights reserved. +* +* BSD-3-Clause +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +* @file bmi160_defs.h +* @date 2021-10-05 +* @version v3.9.2 +* +*/ + +#ifndef BMI160_DEFS_H_ +#define BMI160_DEFS_H_ + +/*************************** C types headers *****************************/ +#ifdef __KERNEL__ +#include +#include +#else +#include +#include +#endif + +/*************************** Common macros *****************************/ + +#if !defined(UINT8_C) && !defined(INT8_C) +#define INT8_C(x) S8_C(x) +#define UINT8_C(x) U8_C(x) +#endif + +#if !defined(UINT16_C) && !defined(INT16_C) +#define INT16_C(x) S16_C(x) +#define UINT16_C(x) U16_C(x) +#endif + +#if !defined(INT32_C) && !defined(UINT32_C) +#define INT32_C(x) S32_C(x) +#define UINT32_C(x) U32_C(x) +#endif + +#if !defined(INT64_C) && !defined(UINT64_C) +#define INT64_C(x) S64_C(x) +#define UINT64_C(x) U64_C(x) +#endif + +/**@}*/ +/**\name C standard macros */ +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void*)0) +#endif +#endif + +/*************************** Sensor macros *****************************/ +/* Test for an endian machine */ +#ifndef __ORDER_LITTLE_ENDIAN__ +#define __ORDER_LITTLE_ENDIAN__ 0 +#endif + +#ifndef __BYTE_ORDER__ +#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ +#endif + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1 +#endif +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 1 +#endif +#else +#error "Code does not support Endian format of the processor" +#endif + +/** Mask definitions */ +#define BMI160_ACCEL_BW_MASK UINT8_C(0x70) +#define BMI160_ACCEL_ODR_MASK UINT8_C(0x0F) +#define BMI160_ACCEL_UNDERSAMPLING_MASK UINT8_C(0x80) +#define BMI160_ACCEL_RANGE_MASK UINT8_C(0x0F) +#define BMI160_GYRO_BW_MASK UINT8_C(0x30) +#define BMI160_GYRO_ODR_MASK UINT8_C(0x0F) +#define BMI160_GYRO_RANGE_MASK UINT8_C(0x07) + +#define BMI160_ACCEL_BW_POS UINT8_C(4) +#define BMI160_GYRO_BW_POS UINT8_C(4) + +/** Mask definitions for INT_EN registers */ +#define BMI160_ANY_MOTION_X_INT_EN_MASK UINT8_C(0x01) +#define BMI160_HIGH_G_X_INT_EN_MASK UINT8_C(0x01) +#define BMI160_NO_MOTION_X_INT_EN_MASK UINT8_C(0x01) +#define BMI160_ANY_MOTION_Y_INT_EN_MASK UINT8_C(0x02) +#define BMI160_HIGH_G_Y_INT_EN_MASK UINT8_C(0x02) +#define BMI160_NO_MOTION_Y_INT_EN_MASK UINT8_C(0x02) +#define BMI160_ANY_MOTION_Z_INT_EN_MASK UINT8_C(0x04) +#define BMI160_HIGH_G_Z_INT_EN_MASK UINT8_C(0x04) +#define BMI160_NO_MOTION_Z_INT_EN_MASK UINT8_C(0x04) +#define BMI160_SIG_MOTION_INT_EN_MASK UINT8_C(0x07) +#define BMI160_ANY_MOTION_ALL_INT_EN_MASK UINT8_C(0x07) +#define BMI160_STEP_DETECT_INT_EN_MASK UINT8_C(0x08) +#define BMI160_DOUBLE_TAP_INT_EN_MASK UINT8_C(0x10) +#define BMI160_SINGLE_TAP_INT_EN_MASK UINT8_C(0x20) +#define BMI160_FIFO_FULL_INT_EN_MASK UINT8_C(0x20) +#define BMI160_ORIENT_INT_EN_MASK UINT8_C(0x40) +#define BMI160_FIFO_WATERMARK_INT_EN_MASK UINT8_C(0x40) +#define BMI160_LOW_G_INT_EN_MASK UINT8_C(0x08) +#define BMI160_STEP_DETECT_EN_MASK UINT8_C(0x08) +#define BMI160_FLAT_INT_EN_MASK UINT8_C(0x80) +#define BMI160_DATA_RDY_INT_EN_MASK UINT8_C(0x10) + +/** PMU status Macros */ +#define BMI160_AUX_PMU_SUSPEND UINT8_C(0x00) +#define BMI160_AUX_PMU_NORMAL UINT8_C(0x01) +#define BMI160_AUX_PMU_LOW_POWER UINT8_C(0x02) + +#define BMI160_GYRO_PMU_SUSPEND UINT8_C(0x00) +#define BMI160_GYRO_PMU_NORMAL UINT8_C(0x01) +#define BMI160_GYRO_PMU_FSU UINT8_C(0x03) + +#define BMI160_ACCEL_PMU_SUSPEND UINT8_C(0x00) +#define BMI160_ACCEL_PMU_NORMAL UINT8_C(0x01) +#define BMI160_ACCEL_PMU_LOW_POWER UINT8_C(0x02) + +/** Mask definitions for INT_OUT_CTRL register */ +#define BMI160_INT1_EDGE_CTRL_MASK UINT8_C(0x01) +#define BMI160_INT1_OUTPUT_MODE_MASK UINT8_C(0x04) +#define BMI160_INT1_OUTPUT_TYPE_MASK UINT8_C(0x02) +#define BMI160_INT1_OUTPUT_EN_MASK UINT8_C(0x08) +#define BMI160_INT2_EDGE_CTRL_MASK UINT8_C(0x10) +#define BMI160_INT2_OUTPUT_MODE_MASK UINT8_C(0x40) +#define BMI160_INT2_OUTPUT_TYPE_MASK UINT8_C(0x20) +#define BMI160_INT2_OUTPUT_EN_MASK UINT8_C(0x80) + +/** Mask definitions for INT_LATCH register */ +#define BMI160_INT1_INPUT_EN_MASK UINT8_C(0x10) +#define BMI160_INT2_INPUT_EN_MASK UINT8_C(0x20) +#define BMI160_INT_LATCH_MASK UINT8_C(0x0F) + +/** Mask definitions for INT_MAP register */ +#define BMI160_INT1_LOW_G_MASK UINT8_C(0x01) +#define BMI160_INT1_HIGH_G_MASK UINT8_C(0x02) +#define BMI160_INT1_SLOPE_MASK UINT8_C(0x04) +#define BMI160_INT1_NO_MOTION_MASK UINT8_C(0x08) +#define BMI160_INT1_DOUBLE_TAP_MASK UINT8_C(0x10) +#define BMI160_INT1_SINGLE_TAP_MASK UINT8_C(0x20) +#define BMI160_INT1_FIFO_FULL_MASK UINT8_C(0x20) +#define BMI160_INT1_FIFO_WM_MASK UINT8_C(0x40) +#define BMI160_INT1_ORIENT_MASK UINT8_C(0x40) +#define BMI160_INT1_FLAT_MASK UINT8_C(0x80) +#define BMI160_INT1_DATA_READY_MASK UINT8_C(0x80) +#define BMI160_INT2_LOW_G_MASK UINT8_C(0x01) +#define BMI160_INT1_LOW_STEP_DETECT_MASK UINT8_C(0x01) +#define BMI160_INT2_LOW_STEP_DETECT_MASK UINT8_C(0x01) +#define BMI160_INT2_HIGH_G_MASK UINT8_C(0x02) +#define BMI160_INT2_FIFO_FULL_MASK UINT8_C(0x02) +#define BMI160_INT2_FIFO_WM_MASK UINT8_C(0x04) +#define BMI160_INT2_SLOPE_MASK UINT8_C(0x04) +#define BMI160_INT2_DATA_READY_MASK UINT8_C(0x08) +#define BMI160_INT2_NO_MOTION_MASK UINT8_C(0x08) +#define BMI160_INT2_DOUBLE_TAP_MASK UINT8_C(0x10) +#define BMI160_INT2_SINGLE_TAP_MASK UINT8_C(0x20) +#define BMI160_INT2_ORIENT_MASK UINT8_C(0x40) +#define BMI160_INT2_FLAT_MASK UINT8_C(0x80) + +/** Mask definitions for INT_DATA register */ +#define BMI160_TAP_SRC_INT_MASK UINT8_C(0x08) +#define BMI160_LOW_HIGH_SRC_INT_MASK UINT8_C(0x80) +#define BMI160_MOTION_SRC_INT_MASK UINT8_C(0x80) + +/** Mask definitions for INT_MOTION register */ +#define BMI160_SLOPE_INT_DUR_MASK UINT8_C(0x03) +#define BMI160_NO_MOTION_INT_DUR_MASK UINT8_C(0xFC) +#define BMI160_NO_MOTION_SEL_BIT_MASK UINT8_C(0x01) + +/** Mask definitions for INT_TAP register */ +#define BMI160_TAP_DUR_MASK UINT8_C(0x07) +#define BMI160_TAP_SHOCK_DUR_MASK UINT8_C(0x40) +#define BMI160_TAP_QUIET_DUR_MASK UINT8_C(0x80) +#define BMI160_TAP_THRES_MASK UINT8_C(0x1F) + +/** Mask definitions for INT_FLAT register */ +#define BMI160_FLAT_THRES_MASK UINT8_C(0x3F) +#define BMI160_FLAT_HOLD_TIME_MASK UINT8_C(0x30) +#define BMI160_FLAT_HYST_MASK UINT8_C(0x07) + +/** Mask definitions for INT_LOWHIGH register */ +#define BMI160_LOW_G_HYST_MASK UINT8_C(0x03) +#define BMI160_LOW_G_LOW_MODE_MASK UINT8_C(0x04) +#define BMI160_HIGH_G_HYST_MASK UINT8_C(0xC0) + +/** Mask definitions for INT_SIG_MOTION register */ +#define BMI160_SIG_MOTION_SEL_MASK UINT8_C(0x02) +#define BMI160_SIG_MOTION_SKIP_MASK UINT8_C(0x0C) +#define BMI160_SIG_MOTION_PROOF_MASK UINT8_C(0x30) + +/** Mask definitions for INT_ORIENT register */ +#define BMI160_ORIENT_MODE_MASK UINT8_C(0x03) +#define BMI160_ORIENT_BLOCK_MASK UINT8_C(0x0C) +#define BMI160_ORIENT_HYST_MASK UINT8_C(0xF0) +#define BMI160_ORIENT_THETA_MASK UINT8_C(0x3F) +#define BMI160_ORIENT_UD_ENABLE UINT8_C(0x40) +#define BMI160_AXES_EN_MASK UINT8_C(0x80) + +/** Mask definitions for FIFO_CONFIG register */ +#define BMI160_FIFO_GYRO UINT8_C(0x80) +#define BMI160_FIFO_ACCEL UINT8_C(0x40) +#define BMI160_FIFO_AUX UINT8_C(0x20) +#define BMI160_FIFO_TAG_INT1 UINT8_C(0x08) +#define BMI160_FIFO_TAG_INT2 UINT8_C(0x04) +#define BMI160_FIFO_TIME UINT8_C(0x02) +#define BMI160_FIFO_HEADER UINT8_C(0x10) +#define BMI160_FIFO_CONFIG_1_MASK UINT8_C(0xFE) + +/** Mask definitions for STEP_CONF register */ +#define BMI160_STEP_COUNT_EN_BIT_MASK UINT8_C(0x08) +#define BMI160_STEP_DETECT_MIN_THRES_MASK UINT8_C(0x18) +#define BMI160_STEP_DETECT_STEPTIME_MIN_MASK UINT8_C(0x07) +#define BMI160_STEP_MIN_BUF_MASK UINT8_C(0x07) + +/** Mask definition for FIFO Header Data Tag */ +#define BMI160_FIFO_TAG_INTR_MASK UINT8_C(0xFC) + +/** Fifo byte counter mask definitions */ +#define BMI160_FIFO_BYTE_COUNTER_MASK UINT8_C(0x07) + +/** Enable/disable bit value */ +#define BMI160_ENABLE UINT8_C(0x01) +#define BMI160_DISABLE UINT8_C(0x00) + +/** Latch Duration */ +#define BMI160_LATCH_DUR_NONE UINT8_C(0x00) +#define BMI160_LATCH_DUR_312_5_MICRO_SEC UINT8_C(0x01) +#define BMI160_LATCH_DUR_625_MICRO_SEC UINT8_C(0x02) +#define BMI160_LATCH_DUR_1_25_MILLI_SEC UINT8_C(0x03) +#define BMI160_LATCH_DUR_2_5_MILLI_SEC UINT8_C(0x04) +#define BMI160_LATCH_DUR_5_MILLI_SEC UINT8_C(0x05) +#define BMI160_LATCH_DUR_10_MILLI_SEC UINT8_C(0x06) +#define BMI160_LATCH_DUR_20_MILLI_SEC UINT8_C(0x07) +#define BMI160_LATCH_DUR_40_MILLI_SEC UINT8_C(0x08) +#define BMI160_LATCH_DUR_80_MILLI_SEC UINT8_C(0x09) +#define BMI160_LATCH_DUR_160_MILLI_SEC UINT8_C(0x0A) +#define BMI160_LATCH_DUR_320_MILLI_SEC UINT8_C(0x0B) +#define BMI160_LATCH_DUR_640_MILLI_SEC UINT8_C(0x0C) +#define BMI160_LATCH_DUR_1_28_SEC UINT8_C(0x0D) +#define BMI160_LATCH_DUR_2_56_SEC UINT8_C(0x0E) +#define BMI160_LATCHED UINT8_C(0x0F) + +/** BMI160 Register map */ +#define BMI160_CHIP_ID_ADDR UINT8_C(0x00) +#define BMI160_ERROR_REG_ADDR UINT8_C(0x02) +#define BMI160_PMU_STATUS_ADDR UINT8_C(0x03) +#define BMI160_AUX_DATA_ADDR UINT8_C(0x04) +#define BMI160_GYRO_DATA_ADDR UINT8_C(0x0C) +#define BMI160_ACCEL_DATA_ADDR UINT8_C(0x12) +#define BMI160_STATUS_ADDR UINT8_C(0x1B) +#define BMI160_INT_STATUS_ADDR UINT8_C(0x1C) +#define BMI160_FIFO_LENGTH_ADDR UINT8_C(0x22) +#define BMI160_FIFO_DATA_ADDR UINT8_C(0x24) +#define BMI160_ACCEL_CONFIG_ADDR UINT8_C(0x40) +#define BMI160_ACCEL_RANGE_ADDR UINT8_C(0x41) +#define BMI160_GYRO_CONFIG_ADDR UINT8_C(0x42) +#define BMI160_GYRO_RANGE_ADDR UINT8_C(0x43) +#define BMI160_AUX_ODR_ADDR UINT8_C(0x44) +#define BMI160_FIFO_DOWN_ADDR UINT8_C(0x45) +#define BMI160_FIFO_CONFIG_0_ADDR UINT8_C(0x46) +#define BMI160_FIFO_CONFIG_1_ADDR UINT8_C(0x47) +#define BMI160_AUX_IF_0_ADDR UINT8_C(0x4B) +#define BMI160_AUX_IF_1_ADDR UINT8_C(0x4C) +#define BMI160_AUX_IF_2_ADDR UINT8_C(0x4D) +#define BMI160_AUX_IF_3_ADDR UINT8_C(0x4E) +#define BMI160_AUX_IF_4_ADDR UINT8_C(0x4F) +#define BMI160_INT_ENABLE_0_ADDR UINT8_C(0x50) +#define BMI160_INT_ENABLE_1_ADDR UINT8_C(0x51) +#define BMI160_INT_ENABLE_2_ADDR UINT8_C(0x52) +#define BMI160_INT_OUT_CTRL_ADDR UINT8_C(0x53) +#define BMI160_INT_LATCH_ADDR UINT8_C(0x54) +#define BMI160_INT_MAP_0_ADDR UINT8_C(0x55) +#define BMI160_INT_MAP_1_ADDR UINT8_C(0x56) +#define BMI160_INT_MAP_2_ADDR UINT8_C(0x57) +#define BMI160_INT_DATA_0_ADDR UINT8_C(0x58) +#define BMI160_INT_DATA_1_ADDR UINT8_C(0x59) +#define BMI160_INT_LOWHIGH_0_ADDR UINT8_C(0x5A) +#define BMI160_INT_LOWHIGH_1_ADDR UINT8_C(0x5B) +#define BMI160_INT_LOWHIGH_2_ADDR UINT8_C(0x5C) +#define BMI160_INT_LOWHIGH_3_ADDR UINT8_C(0x5D) +#define BMI160_INT_LOWHIGH_4_ADDR UINT8_C(0x5E) +#define BMI160_INT_MOTION_0_ADDR UINT8_C(0x5F) +#define BMI160_INT_MOTION_1_ADDR UINT8_C(0x60) +#define BMI160_INT_MOTION_2_ADDR UINT8_C(0x61) +#define BMI160_INT_MOTION_3_ADDR UINT8_C(0x62) +#define BMI160_INT_TAP_0_ADDR UINT8_C(0x63) +#define BMI160_INT_TAP_1_ADDR UINT8_C(0x64) +#define BMI160_INT_ORIENT_0_ADDR UINT8_C(0x65) +#define BMI160_INT_ORIENT_1_ADDR UINT8_C(0x66) +#define BMI160_INT_FLAT_0_ADDR UINT8_C(0x67) +#define BMI160_INT_FLAT_1_ADDR UINT8_C(0x68) +#define BMI160_FOC_CONF_ADDR UINT8_C(0x69) +#define BMI160_CONF_ADDR UINT8_C(0x6A) + +#define BMI160_IF_CONF_ADDR UINT8_C(0x6B) +#define BMI160_SELF_TEST_ADDR UINT8_C(0x6D) +#define BMI160_OFFSET_ADDR UINT8_C(0x71) +#define BMI160_OFFSET_CONF_ADDR UINT8_C(0x77) +#define BMI160_INT_STEP_CNT_0_ADDR UINT8_C(0x78) +#define BMI160_INT_STEP_CONFIG_0_ADDR UINT8_C(0x7A) +#define BMI160_INT_STEP_CONFIG_1_ADDR UINT8_C(0x7B) +#define BMI160_COMMAND_REG_ADDR UINT8_C(0x7E) +#define BMI160_SPI_COMM_TEST_ADDR UINT8_C(0x7F) +#define BMI160_INTL_PULLUP_CONF_ADDR UINT8_C(0x85) + +/** Error code definitions */ +#define BMI160_OK INT8_C(0) +#define BMI160_E_NULL_PTR INT8_C(-1) +#define BMI160_E_COM_FAIL INT8_C(-2) +#define BMI160_E_DEV_NOT_FOUND INT8_C(-3) +#define BMI160_E_OUT_OF_RANGE INT8_C(-4) +#define BMI160_E_INVALID_INPUT INT8_C(-5) +#define BMI160_E_ACCEL_ODR_BW_INVALID INT8_C(-6) +#define BMI160_E_GYRO_ODR_BW_INVALID INT8_C(-7) +#define BMI160_E_LWP_PRE_FLTR_INT_INVALID INT8_C(-8) +#define BMI160_E_LWP_PRE_FLTR_INVALID INT8_C(-9) +#define BMI160_E_AUX_NOT_FOUND INT8_C(-10) +#define BMI160_E_FOC_FAILURE INT8_C(-11) +#define BMI160_E_READ_WRITE_LENGTH_INVALID INT8_C(-12) +#define BMI160_E_INVALID_CONFIG INT8_C(-13) + +/**\name API warning codes */ +#define BMI160_W_GYRO_SELF_TEST_FAIL INT8_C(1) +#define BMI160_W_ACCEl_SELF_TEST_FAIL INT8_C(2) + +/** BMI160 unique chip identifier */ +#define BMI160_CHIP_ID UINT8_C(0xD1) + +/** Soft reset command */ +#define BMI160_SOFT_RESET_CMD UINT8_C(0xb6) +#define BMI160_SOFT_RESET_DELAY_MS UINT8_C(1) + +/** Start FOC command */ +#define BMI160_START_FOC_CMD UINT8_C(0x03) + +/** NVM backup enabling command */ +#define BMI160_NVM_BACKUP_EN UINT8_C(0xA0) + +/* Delay in ms settings */ +#define BMI160_ACCEL_DELAY_MS UINT8_C(5) +#define BMI160_GYRO_DELAY_MS UINT8_C(80) +#define BMI160_ONE_MS_DELAY UINT8_C(1) +#define BMI160_AUX_COM_DELAY UINT8_C(10) +#define BMI160_GYRO_SELF_TEST_DELAY UINT8_C(20) +#define BMI160_ACCEL_SELF_TEST_DELAY UINT8_C(50) + +/** Self test configurations */ +#define BMI160_ACCEL_SELF_TEST_CONFIG UINT8_C(0x2C) +#define BMI160_ACCEL_SELF_TEST_POSITIVE_EN UINT8_C(0x0D) +#define BMI160_ACCEL_SELF_TEST_NEGATIVE_EN UINT8_C(0x09) +#define BMI160_ACCEL_SELF_TEST_LIMIT UINT16_C(8192) + +/** Power mode settings */ +/* Accel power mode */ +#define BMI160_ACCEL_NORMAL_MODE UINT8_C(0x11) +#define BMI160_ACCEL_LOWPOWER_MODE UINT8_C(0x12) +#define BMI160_ACCEL_SUSPEND_MODE UINT8_C(0x10) + +/* Gyro power mode */ +#define BMI160_GYRO_SUSPEND_MODE UINT8_C(0x14) +#define BMI160_GYRO_NORMAL_MODE UINT8_C(0x15) +#define BMI160_GYRO_FASTSTARTUP_MODE UINT8_C(0x17) + +/* Aux power mode */ +#define BMI160_AUX_SUSPEND_MODE UINT8_C(0x18) +#define BMI160_AUX_NORMAL_MODE UINT8_C(0x19) +#define BMI160_AUX_LOWPOWER_MODE UINT8_C(0x1A) + +/** Range settings */ +/* Accel Range */ +#define BMI160_ACCEL_RANGE_2G UINT8_C(0x03) +#define BMI160_ACCEL_RANGE_4G UINT8_C(0x05) +#define BMI160_ACCEL_RANGE_8G UINT8_C(0x08) +#define BMI160_ACCEL_RANGE_16G UINT8_C(0x0C) + +/* Gyro Range */ +#define BMI160_GYRO_RANGE_2000_DPS UINT8_C(0x00) +#define BMI160_GYRO_RANGE_1000_DPS UINT8_C(0x01) +#define BMI160_GYRO_RANGE_500_DPS UINT8_C(0x02) +#define BMI160_GYRO_RANGE_250_DPS UINT8_C(0x03) +#define BMI160_GYRO_RANGE_125_DPS UINT8_C(0x04) + +/** Bandwidth settings */ +/* Accel Bandwidth */ +#define BMI160_ACCEL_BW_OSR4_AVG1 UINT8_C(0x00) +#define BMI160_ACCEL_BW_OSR2_AVG2 UINT8_C(0x01) +#define BMI160_ACCEL_BW_NORMAL_AVG4 UINT8_C(0x02) +#define BMI160_ACCEL_BW_RES_AVG8 UINT8_C(0x03) +#define BMI160_ACCEL_BW_RES_AVG16 UINT8_C(0x04) +#define BMI160_ACCEL_BW_RES_AVG32 UINT8_C(0x05) +#define BMI160_ACCEL_BW_RES_AVG64 UINT8_C(0x06) +#define BMI160_ACCEL_BW_RES_AVG128 UINT8_C(0x07) + +#define BMI160_GYRO_BW_OSR4_MODE UINT8_C(0x00) +#define BMI160_GYRO_BW_OSR2_MODE UINT8_C(0x01) +#define BMI160_GYRO_BW_NORMAL_MODE UINT8_C(0x02) + +/* Output Data Rate settings */ +/* Accel Output data rate */ +#define BMI160_ACCEL_ODR_RESERVED UINT8_C(0x00) +#define BMI160_ACCEL_ODR_0_78HZ UINT8_C(0x01) +#define BMI160_ACCEL_ODR_1_56HZ UINT8_C(0x02) +#define BMI160_ACCEL_ODR_3_12HZ UINT8_C(0x03) +#define BMI160_ACCEL_ODR_6_25HZ UINT8_C(0x04) +#define BMI160_ACCEL_ODR_12_5HZ UINT8_C(0x05) +#define BMI160_ACCEL_ODR_25HZ UINT8_C(0x06) +#define BMI160_ACCEL_ODR_50HZ UINT8_C(0x07) +#define BMI160_ACCEL_ODR_100HZ UINT8_C(0x08) +#define BMI160_ACCEL_ODR_200HZ UINT8_C(0x09) +#define BMI160_ACCEL_ODR_400HZ UINT8_C(0x0A) +#define BMI160_ACCEL_ODR_800HZ UINT8_C(0x0B) +#define BMI160_ACCEL_ODR_1600HZ UINT8_C(0x0C) +#define BMI160_ACCEL_ODR_RESERVED0 UINT8_C(0x0D) +#define BMI160_ACCEL_ODR_RESERVED1 UINT8_C(0x0E) +#define BMI160_ACCEL_ODR_RESERVED2 UINT8_C(0x0F) + +/* Gyro Output data rate */ +#define BMI160_GYRO_ODR_RESERVED UINT8_C(0x00) +#define BMI160_GYRO_ODR_25HZ UINT8_C(0x06) +#define BMI160_GYRO_ODR_50HZ UINT8_C(0x07) +#define BMI160_GYRO_ODR_100HZ UINT8_C(0x08) +#define BMI160_GYRO_ODR_200HZ UINT8_C(0x09) +#define BMI160_GYRO_ODR_400HZ UINT8_C(0x0A) +#define BMI160_GYRO_ODR_800HZ UINT8_C(0x0B) +#define BMI160_GYRO_ODR_1600HZ UINT8_C(0x0C) +#define BMI160_GYRO_ODR_3200HZ UINT8_C(0x0D) + +/* Auxiliary sensor Output data rate */ +#define BMI160_AUX_ODR_RESERVED UINT8_C(0x00) +#define BMI160_AUX_ODR_0_78HZ UINT8_C(0x01) +#define BMI160_AUX_ODR_1_56HZ UINT8_C(0x02) +#define BMI160_AUX_ODR_3_12HZ UINT8_C(0x03) +#define BMI160_AUX_ODR_6_25HZ UINT8_C(0x04) +#define BMI160_AUX_ODR_12_5HZ UINT8_C(0x05) +#define BMI160_AUX_ODR_25HZ UINT8_C(0x06) +#define BMI160_AUX_ODR_50HZ UINT8_C(0x07) +#define BMI160_AUX_ODR_100HZ UINT8_C(0x08) +#define BMI160_AUX_ODR_200HZ UINT8_C(0x09) +#define BMI160_AUX_ODR_400HZ UINT8_C(0x0A) +#define BMI160_AUX_ODR_800HZ UINT8_C(0x0B) + +/** FIFO_CONFIG Definitions */ +#define BMI160_FIFO_TIME_ENABLE UINT8_C(0x02) +#define BMI160_FIFO_TAG_INT2_ENABLE UINT8_C(0x04) +#define BMI160_FIFO_TAG_INT1_ENABLE UINT8_C(0x08) +#define BMI160_FIFO_HEAD_ENABLE UINT8_C(0x10) +#define BMI160_FIFO_M_ENABLE UINT8_C(0x20) +#define BMI160_FIFO_A_ENABLE UINT8_C(0x40) +#define BMI160_FIFO_M_A_ENABLE UINT8_C(0x60) +#define BMI160_FIFO_G_ENABLE UINT8_C(0x80) +#define BMI160_FIFO_M_G_ENABLE UINT8_C(0xA0) +#define BMI160_FIFO_G_A_ENABLE UINT8_C(0xC0) +#define BMI160_FIFO_M_G_A_ENABLE UINT8_C(0xE0) + +/* Macro to specify the number of bytes over-read from the + * FIFO in order to get the sensor time at the end of FIFO */ +#ifndef BMI160_FIFO_BYTES_OVERREAD +#define BMI160_FIFO_BYTES_OVERREAD UINT8_C(25) +#endif + +/* Accel, gyro and aux. sensor length and also their combined + * length definitions in FIFO */ +#define BMI160_FIFO_G_LENGTH UINT8_C(6) +#define BMI160_FIFO_A_LENGTH UINT8_C(6) +#define BMI160_FIFO_M_LENGTH UINT8_C(8) +#define BMI160_FIFO_GA_LENGTH UINT8_C(12) +#define BMI160_FIFO_MA_LENGTH UINT8_C(14) +#define BMI160_FIFO_MG_LENGTH UINT8_C(14) +#define BMI160_FIFO_MGA_LENGTH UINT8_C(20) + +/** FIFO Header Data definitions */ +#define BMI160_FIFO_HEAD_SKIP_FRAME UINT8_C(0x40) +#define BMI160_FIFO_HEAD_SENSOR_TIME UINT8_C(0x44) +#define BMI160_FIFO_HEAD_INPUT_CONFIG UINT8_C(0x48) +#define BMI160_FIFO_HEAD_OVER_READ UINT8_C(0x80) +#define BMI160_FIFO_HEAD_A UINT8_C(0x84) +#define BMI160_FIFO_HEAD_G UINT8_C(0x88) +#define BMI160_FIFO_HEAD_G_A UINT8_C(0x8C) +#define BMI160_FIFO_HEAD_M UINT8_C(0x90) +#define BMI160_FIFO_HEAD_M_A UINT8_C(0x94) +#define BMI160_FIFO_HEAD_M_G UINT8_C(0x98) +#define BMI160_FIFO_HEAD_M_G_A UINT8_C(0x9C) + +/** FIFO sensor time length definitions */ +#define BMI160_SENSOR_TIME_LENGTH UINT8_C(3) + +/** FIFO DOWN selection */ +/* Accel fifo down-sampling values*/ +#define BMI160_ACCEL_FIFO_DOWN_ZERO UINT8_C(0x00) +#define BMI160_ACCEL_FIFO_DOWN_ONE UINT8_C(0x10) +#define BMI160_ACCEL_FIFO_DOWN_TWO UINT8_C(0x20) +#define BMI160_ACCEL_FIFO_DOWN_THREE UINT8_C(0x30) +#define BMI160_ACCEL_FIFO_DOWN_FOUR UINT8_C(0x40) +#define BMI160_ACCEL_FIFO_DOWN_FIVE UINT8_C(0x50) +#define BMI160_ACCEL_FIFO_DOWN_SIX UINT8_C(0x60) +#define BMI160_ACCEL_FIFO_DOWN_SEVEN UINT8_C(0x70) + +/* Gyro fifo down-smapling values*/ +#define BMI160_GYRO_FIFO_DOWN_ZERO UINT8_C(0x00) +#define BMI160_GYRO_FIFO_DOWN_ONE UINT8_C(0x01) +#define BMI160_GYRO_FIFO_DOWN_TWO UINT8_C(0x02) +#define BMI160_GYRO_FIFO_DOWN_THREE UINT8_C(0x03) +#define BMI160_GYRO_FIFO_DOWN_FOUR UINT8_C(0x04) +#define BMI160_GYRO_FIFO_DOWN_FIVE UINT8_C(0x05) +#define BMI160_GYRO_FIFO_DOWN_SIX UINT8_C(0x06) +#define BMI160_GYRO_FIFO_DOWN_SEVEN UINT8_C(0x07) + +/* Accel Fifo filter enable*/ +#define BMI160_ACCEL_FIFO_FILT_EN UINT8_C(0x80) + +/* Gyro Fifo filter enable*/ +#define BMI160_GYRO_FIFO_FILT_EN UINT8_C(0x08) + +/** Definitions to check validity of FIFO frames */ +#define FIFO_CONFIG_MSB_CHECK UINT8_C(0x80) +#define FIFO_CONFIG_LSB_CHECK UINT8_C(0x00) + +/*! BMI160 accel FOC configurations */ +#define BMI160_FOC_ACCEL_DISABLED UINT8_C(0x00) +#define BMI160_FOC_ACCEL_POSITIVE_G UINT8_C(0x01) +#define BMI160_FOC_ACCEL_NEGATIVE_G UINT8_C(0x02) +#define BMI160_FOC_ACCEL_0G UINT8_C(0x03) + +/** Array Parameter DefinItions */ +#define BMI160_SENSOR_TIME_LSB_BYTE UINT8_C(0) +#define BMI160_SENSOR_TIME_XLSB_BYTE UINT8_C(1) +#define BMI160_SENSOR_TIME_MSB_BYTE UINT8_C(2) + +/** Interface settings */ +#define BMI160_SPI_INTF UINT8_C(1) +#define BMI160_I2C_INTF UINT8_C(0) +#define BMI160_SPI_RD_MASK UINT8_C(0x80) +#define BMI160_SPI_WR_MASK UINT8_C(0x7F) + +/* Sensor & time select definition*/ +#define BMI160_ACCEL_SEL UINT8_C(0x01) +#define BMI160_GYRO_SEL UINT8_C(0x02) +#define BMI160_TIME_SEL UINT8_C(0x04) + +/* Sensor select mask*/ +#define BMI160_SEN_SEL_MASK UINT8_C(0x07) + +/* Error code mask */ +#define BMI160_ERR_REG_MASK UINT8_C(0x0F) + +/* BMI160 I2C address */ +#define BMI160_I2C_ADDR UINT8_C(0x68) + +/* BMI160 secondary IF address */ +#define BMI160_AUX_BMM150_I2C_ADDR UINT8_C(0x10) + +/** BMI160 Length definitions */ +#define BMI160_ONE UINT8_C(1) +#define BMI160_TWO UINT8_C(2) +#define BMI160_THREE UINT8_C(3) +#define BMI160_FOUR UINT8_C(4) +#define BMI160_FIVE UINT8_C(5) + +/** BMI160 fifo level Margin */ +#define BMI160_FIFO_LEVEL_MARGIN UINT8_C(16) + +/** BMI160 fifo flush Command */ +#define BMI160_FIFO_FLUSH_VALUE UINT8_C(0xB0) + +/** BMI160 offset values for xyz axes of accel */ +#define BMI160_ACCEL_MIN_OFFSET INT8_C(-128) +#define BMI160_ACCEL_MAX_OFFSET INT8_C(127) + +/** BMI160 offset values for xyz axes of gyro */ +#define BMI160_GYRO_MIN_OFFSET INT16_C(-512) +#define BMI160_GYRO_MAX_OFFSET INT16_C(511) + +/** BMI160 fifo full interrupt position and mask */ +#define BMI160_FIFO_FULL_INT_POS UINT8_C(5) +#define BMI160_FIFO_FULL_INT_MSK UINT8_C(0x20) +#define BMI160_FIFO_WTM_INT_POS UINT8_C(6) +#define BMI160_FIFO_WTM_INT_MSK UINT8_C(0x40) + +#define BMI160_FIFO_FULL_INT_PIN1_POS UINT8_C(5) +#define BMI160_FIFO_FULL_INT_PIN1_MSK UINT8_C(0x20) +#define BMI160_FIFO_FULL_INT_PIN2_POS UINT8_C(1) +#define BMI160_FIFO_FULL_INT_PIN2_MSK UINT8_C(0x02) + +#define BMI160_FIFO_WTM_INT_PIN1_POS UINT8_C(6) +#define BMI160_FIFO_WTM_INT_PIN1_MSK UINT8_C(0x40) +#define BMI160_FIFO_WTM_INT_PIN2_POS UINT8_C(2) +#define BMI160_FIFO_WTM_INT_PIN2_MSK UINT8_C(0x04) + +#define BMI160_MANUAL_MODE_EN_POS UINT8_C(7) +#define BMI160_MANUAL_MODE_EN_MSK UINT8_C(0x80) +#define BMI160_AUX_READ_BURST_POS UINT8_C(0) +#define BMI160_AUX_READ_BURST_MSK UINT8_C(0x03) + +#define BMI160_GYRO_SELF_TEST_POS UINT8_C(4) +#define BMI160_GYRO_SELF_TEST_MSK UINT8_C(0x10) +#define BMI160_GYRO_SELF_TEST_STATUS_POS UINT8_C(1) +#define BMI160_GYRO_SELF_TEST_STATUS_MSK UINT8_C(0x02) + +#define BMI160_GYRO_FOC_EN_POS UINT8_C(6) +#define BMI160_GYRO_FOC_EN_MSK UINT8_C(0x40) + +#define BMI160_ACCEL_FOC_X_CONF_POS UINT8_C(4) +#define BMI160_ACCEL_FOC_X_CONF_MSK UINT8_C(0x30) + +#define BMI160_ACCEL_FOC_Y_CONF_POS UINT8_C(2) +#define BMI160_ACCEL_FOC_Y_CONF_MSK UINT8_C(0x0C) + +#define BMI160_ACCEL_FOC_Z_CONF_MSK UINT8_C(0x03) + +#define BMI160_FOC_STATUS_POS UINT8_C(3) +#define BMI160_FOC_STATUS_MSK UINT8_C(0x08) + +#define BMI160_GYRO_OFFSET_X_MSK UINT8_C(0x03) + +#define BMI160_GYRO_OFFSET_Y_POS UINT8_C(2) +#define BMI160_GYRO_OFFSET_Y_MSK UINT8_C(0x0C) + +#define BMI160_GYRO_OFFSET_Z_POS UINT8_C(4) +#define BMI160_GYRO_OFFSET_Z_MSK UINT8_C(0x30) + +#define BMI160_GYRO_OFFSET_EN_POS UINT8_C(7) +#define BMI160_GYRO_OFFSET_EN_MSK UINT8_C(0x80) + +#define BMI160_ACCEL_OFFSET_EN_POS UINT8_C(6) +#define BMI160_ACCEL_OFFSET_EN_MSK UINT8_C(0x40) + +#define BMI160_GYRO_OFFSET_POS UINT16_C(8) +#define BMI160_GYRO_OFFSET_MSK UINT16_C(0x0300) + +#define BMI160_NVM_UPDATE_POS UINT8_C(1) +#define BMI160_NVM_UPDATE_MSK UINT8_C(0x02) + +#define BMI160_NVM_STATUS_POS UINT8_C(4) +#define BMI160_NVM_STATUS_MSK UINT8_C(0x10) + +#define BMI160_MAG_POWER_MODE_MSK UINT8_C(0x03) + +#define BMI160_ACCEL_POWER_MODE_MSK UINT8_C(0x30) +#define BMI160_ACCEL_POWER_MODE_POS UINT8_C(4) + +#define BMI160_GYRO_POWER_MODE_MSK UINT8_C(0x0C) +#define BMI160_GYRO_POWER_MODE_POS UINT8_C(2) + +/* BIT SLICE GET AND SET FUNCTIONS */ +#define BMI160_GET_BITS(regvar, bitname) ((regvar & bitname##_MSK) >> bitname##_POS) +#define BMI160_SET_BITS(regvar, bitname, val) \ + ((regvar & ~bitname##_MSK) | ((val << bitname##_POS) & bitname##_MSK)) + +#define BMI160_SET_BITS_POS_0(reg_data, bitname, data) \ + ((reg_data & ~(bitname##_MSK)) | (data & bitname##_MSK)) + +#define BMI160_GET_BITS_POS_0(reg_data, bitname) (reg_data & (bitname##_MSK)) + +/**\name UTILITY MACROS */ +#define BMI160_SET_LOW_BYTE UINT16_C(0x00FF) +#define BMI160_SET_HIGH_BYTE UINT16_C(0xFF00) + +#define BMI160_GET_LSB(var) (uint8_t)(var & BMI160_SET_LOW_BYTE) +#define BMI160_GET_MSB(var) (uint8_t)((var & BMI160_SET_HIGH_BYTE) >> 8) + +/*****************************************************************************/ +/* type definitions */ + +/*! + * @brief Bus communication function pointer which should be mapped to + * the platform specific read functions of the user + */ +typedef int8_t ( + *bmi160_read_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint16_t len); + +/*! + * @brief Bus communication function pointer which should be mapped to + * the platform specific write functions of the user + */ +typedef int8_t ( + *bmi160_write_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t* read_data, uint16_t len); +typedef void (*bmi160_delay_fptr_t)(uint32_t period); + +/*************************** Data structures *********************************/ + +/*! + * @brief bmi160 interrupt status selection enum. + */ +enum bmi160_int_status_sel { + BMI160_INT_STATUS_0 = 1, + BMI160_INT_STATUS_1 = 2, + BMI160_INT_STATUS_2 = 4, + BMI160_INT_STATUS_3 = 8, + BMI160_INT_STATUS_ALL = 15 +}; + +/*! + * @brief bmi160 interrupt status bits structure + */ +struct bmi160_int_status_bits { +#ifdef LITTLE_ENDIAN + + uint32_t step : 1; + uint32_t sigmot : 1; + uint32_t anym : 1; + + /* pmu trigger will be handled later */ + uint32_t pmu_trigger_reserved : 1; + uint32_t d_tap : 1; + uint32_t s_tap : 1; + uint32_t orient : 1; + uint32_t flat_int : 1; + uint32_t reserved : 2; + uint32_t high_g : 1; + uint32_t low_g : 1; + uint32_t drdy : 1; + uint32_t ffull : 1; + uint32_t fwm : 1; + uint32_t nomo : 1; + uint32_t anym_first_x : 1; + uint32_t anym_first_y : 1; + uint32_t anym_first_z : 1; + uint32_t anym_sign : 1; + uint32_t tap_first_x : 1; + uint32_t tap_first_y : 1; + uint32_t tap_first_z : 1; + uint32_t tap_sign : 1; + uint32_t high_first_x : 1; + uint32_t high_first_y : 1; + uint32_t high_first_z : 1; + uint32_t high_sign : 1; + uint32_t orient_1_0 : 2; + uint32_t orient_2 : 1; + uint32_t flat : 1; +#else + uint32_t high_first_x : 1; + uint32_t high_first_y : 1; + uint32_t high_first_z : 1; + uint32_t high_sign : 1; + uint32_t orient_1_0 : 2; + uint32_t orient_2 : 1; + uint32_t flat : 1; + uint32_t anym_first_x : 1; + uint32_t anym_first_y : 1; + uint32_t anym_first_z : 1; + uint32_t anym_sign : 1; + uint32_t tap_first_x : 1; + uint32_t tap_first_y : 1; + uint32_t tap_first_z : 1; + uint32_t tap_sign : 1; + uint32_t reserved : 2; + uint32_t high_g : 1; + uint32_t low_g : 1; + uint32_t drdy : 1; + uint32_t ffull : 1; + uint32_t fwm : 1; + uint32_t nomo : 1; + uint32_t step : 1; + uint32_t sigmot : 1; + uint32_t anym : 1; + + /* pmu trigger will be handled later */ + uint32_t pmu_trigger_reserved : 1; + uint32_t d_tap : 1; + uint32_t s_tap : 1; + uint32_t orient : 1; + uint32_t flat_int : 1; +#endif +}; + +/*! + * @brief bmi160 interrupt status structure + */ +union bmi160_int_status { + uint8_t data[4]; + struct bmi160_int_status_bits bit; +}; + +/*! + * @brief bmi160 sensor data structure which comprises of accel data + */ +struct bmi160_sensor_data { + /*! X-axis sensor data */ + int16_t x; + + /*! Y-axis sensor data */ + int16_t y; + + /*! Z-axis sensor data */ + int16_t z; + + /*! sensor time */ + uint32_t sensortime; +}; + +/*! + * @brief bmi160 aux data structure which comprises of 8 bytes of accel data + */ +struct bmi160_aux_data { + /*! Auxiliary data */ + uint8_t data[8]; +}; + +/*! + * @brief bmi160 FOC configuration structure + */ +struct bmi160_foc_conf { + /*! Enabling FOC in gyro + * Assignable macros : + * - BMI160_ENABLE + * - BMI160_DISABLE + */ + uint8_t foc_gyr_en; + + /*! Accel FOC configurations + * Assignable macros : + * - BMI160_FOC_ACCEL_DISABLED + * - BMI160_FOC_ACCEL_POSITIVE_G + * - BMI160_FOC_ACCEL_NEGATIVE_G + * - BMI160_FOC_ACCEL_0G + */ + uint8_t foc_acc_x; + uint8_t foc_acc_y; + uint8_t foc_acc_z; + + /*! Enabling offset compensation for accel in data registers + * Assignable macros : + * - BMI160_ENABLE + * - BMI160_DISABLE + */ + uint8_t acc_off_en; + + /*! Enabling offset compensation for gyro in data registers + * Assignable macros : + * - BMI160_ENABLE + * - BMI160_DISABLE + */ + uint8_t gyro_off_en; +}; + +/*! + * @brief bmi160 accel gyro offsets + */ +struct bmi160_offsets { + /*! Accel offset for x axis */ + int8_t off_acc_x; + + /*! Accel offset for y axis */ + int8_t off_acc_y; + + /*! Accel offset for z axis */ + int8_t off_acc_z; + + /*! Gyro offset for x axis */ + int16_t off_gyro_x; + + /*! Gyro offset for y axis */ + int16_t off_gyro_y; + + /*! Gyro offset for z axis */ + int16_t off_gyro_z; +}; + +/*! + * @brief FIFO aux. sensor data structure + */ +struct bmi160_aux_fifo_data { + /*! The value of aux. sensor x LSB data */ + uint8_t aux_x_lsb; + + /*! The value of aux. sensor x MSB data */ + uint8_t aux_x_msb; + + /*! The value of aux. sensor y LSB data */ + uint8_t aux_y_lsb; + + /*! The value of aux. sensor y MSB data */ + uint8_t aux_y_msb; + + /*! The value of aux. sensor z LSB data */ + uint8_t aux_z_lsb; + + /*! The value of aux. sensor z MSB data */ + uint8_t aux_z_msb; + + /*! The value of aux. sensor r for BMM150 LSB data */ + uint8_t aux_r_y2_lsb; + + /*! The value of aux. sensor r for BMM150 MSB data */ + uint8_t aux_r_y2_msb; +}; + +/*! + * @brief bmi160 sensor select structure + */ +enum bmi160_select_sensor { BMI160_ACCEL_ONLY = 1, BMI160_GYRO_ONLY, BMI160_BOTH_ACCEL_AND_GYRO }; + +/*! + * @brief bmi160 sensor step detector mode structure + */ +enum bmi160_step_detect_mode { + BMI160_STEP_DETECT_NORMAL, + BMI160_STEP_DETECT_SENSITIVE, + BMI160_STEP_DETECT_ROBUST, + + /*! Non recommended User defined setting */ + BMI160_STEP_DETECT_USER_DEFINE +}; + +/*! + * @brief enum for auxiliary burst read selection + */ +enum bmi160_aux_read_len { + BMI160_AUX_READ_LEN_0, + BMI160_AUX_READ_LEN_1, + BMI160_AUX_READ_LEN_2, + BMI160_AUX_READ_LEN_3 +}; + +/*! + * @brief bmi160 sensor configuration structure + */ +struct bmi160_cfg { + /*! power mode */ + uint8_t power; + + /*! output data rate */ + uint8_t odr; + + /*! range */ + uint8_t range; + + /*! bandwidth */ + uint8_t bw; +}; + +/*! + * @brief Aux sensor configuration structure + */ +struct bmi160_aux_cfg { + /*! Aux sensor, 1 - enable 0 - disable */ + uint8_t aux_sensor_enable : 1; + + /*! Aux manual/auto mode status */ + uint8_t manual_enable : 1; + + /*! Aux read burst length */ + uint8_t aux_rd_burst_len : 2; + + /*! output data rate */ + uint8_t aux_odr : 4; + + /*! i2c addr of auxiliary sensor */ + uint8_t aux_i2c_addr; +}; + +/*! + * @brief bmi160 interrupt channel selection structure + */ +enum bmi160_int_channel { + /*! Un-map both channels */ + BMI160_INT_CHANNEL_NONE, + + /*! interrupt Channel 1 */ + BMI160_INT_CHANNEL_1, + + /*! interrupt Channel 2 */ + BMI160_INT_CHANNEL_2, + + /*! Map both channels */ + BMI160_INT_CHANNEL_BOTH +}; +enum bmi160_int_types { + /*! Slope/Any-motion interrupt */ + BMI160_ACC_ANY_MOTION_INT, + + /*! Significant motion interrupt */ + BMI160_ACC_SIG_MOTION_INT, + + /*! Step detector interrupt */ + BMI160_STEP_DETECT_INT, + + /*! double tap interrupt */ + BMI160_ACC_DOUBLE_TAP_INT, + + /*! single tap interrupt */ + BMI160_ACC_SINGLE_TAP_INT, + + /*! orientation interrupt */ + BMI160_ACC_ORIENT_INT, + + /*! flat interrupt */ + BMI160_ACC_FLAT_INT, + + /*! high-g interrupt */ + BMI160_ACC_HIGH_G_INT, + + /*! low-g interrupt */ + BMI160_ACC_LOW_G_INT, + + /*! slow/no-motion interrupt */ + BMI160_ACC_SLOW_NO_MOTION_INT, + + /*! data ready interrupt */ + BMI160_ACC_GYRO_DATA_RDY_INT, + + /*! fifo full interrupt */ + BMI160_ACC_GYRO_FIFO_FULL_INT, + + /*! fifo watermark interrupt */ + BMI160_ACC_GYRO_FIFO_WATERMARK_INT, + + /*! fifo tagging feature support */ + BMI160_FIFO_TAG_INT_PIN +}; + +/*! + * @brief bmi160 active state of any & sig motion interrupt. + */ +enum bmi160_any_sig_motion_active_interrupt_state { + /*! Both any & sig motion are disabled */ + BMI160_BOTH_ANY_SIG_MOTION_DISABLED = -1, + + /*! Any-motion selected */ + BMI160_ANY_MOTION_ENABLED, + + /*! Sig-motion selected */ + BMI160_SIG_MOTION_ENABLED +}; +struct bmi160_acc_tap_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! tap threshold */ + uint16_t tap_thr : 5; + + /*! tap shock */ + uint16_t tap_shock : 1; + + /*! tap quiet */ + uint16_t tap_quiet : 1; + + /*! tap duration */ + uint16_t tap_dur : 3; + + /*! data source 0- filter & 1 pre-filter*/ + uint16_t tap_data_src : 1; + + /*! tap enable, 1 - enable, 0 - disable */ + uint16_t tap_en : 1; +#else + + /*! tap enable, 1 - enable, 0 - disable */ + uint16_t tap_en : 1; + + /*! data source 0- filter & 1 pre-filter*/ + uint16_t tap_data_src : 1; + + /*! tap duration */ + uint16_t tap_dur : 3; + + /*! tap quiet */ + uint16_t tap_quiet : 1; + + /*! tap shock */ + uint16_t tap_shock : 1; + + /*! tap threshold */ + uint16_t tap_thr : 5; +#endif +}; +struct bmi160_acc_any_mot_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! 1 any-motion enable, 0 - any-motion disable */ + uint8_t anymotion_en : 1; + + /*! slope interrupt x, 1 - enable, 0 - disable */ + uint8_t anymotion_x : 1; + + /*! slope interrupt y, 1 - enable, 0 - disable */ + uint8_t anymotion_y : 1; + + /*! slope interrupt z, 1 - enable, 0 - disable */ + uint8_t anymotion_z : 1; + + /*! slope duration */ + uint8_t anymotion_dur : 2; + + /*! data source 0- filter & 1 pre-filter*/ + uint8_t anymotion_data_src : 1; + + /*! slope threshold */ + uint8_t anymotion_thr; +#else + + /*! slope threshold */ + uint8_t anymotion_thr; + + /*! data source 0- filter & 1 pre-filter*/ + uint8_t anymotion_data_src : 1; + + /*! slope duration */ + uint8_t anymotion_dur : 2; + + /*! slope interrupt z, 1 - enable, 0 - disable */ + uint8_t anymotion_z : 1; + + /*! slope interrupt y, 1 - enable, 0 - disable */ + uint8_t anymotion_y : 1; + + /*! slope interrupt x, 1 - enable, 0 - disable */ + uint8_t anymotion_x : 1; + + /*! 1 any-motion enable, 0 - any-motion disable */ + uint8_t anymotion_en : 1; +#endif +}; +struct bmi160_acc_sig_mot_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! skip time of sig-motion interrupt */ + uint8_t sig_mot_skip : 2; + + /*! proof time of sig-motion interrupt */ + uint8_t sig_mot_proof : 2; + + /*! data source 0- filter & 1 pre-filter*/ + uint8_t sig_data_src : 1; + + /*! 1 - enable sig, 0 - disable sig & enable anymotion */ + uint8_t sig_en : 1; + + /*! sig-motion threshold */ + uint8_t sig_mot_thres; +#else + + /*! sig-motion threshold */ + uint8_t sig_mot_thres; + + /*! 1 - enable sig, 0 - disable sig & enable anymotion */ + uint8_t sig_en : 1; + + /*! data source 0- filter & 1 pre-filter*/ + uint8_t sig_data_src : 1; + + /*! proof time of sig-motion interrupt */ + uint8_t sig_mot_proof : 2; + + /*! skip time of sig-motion interrupt */ + uint8_t sig_mot_skip : 2; +#endif +}; +struct bmi160_acc_step_detect_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! 1- step detector enable, 0- step detector disable */ + uint16_t step_detector_en : 1; + + /*! minimum threshold */ + uint16_t min_threshold : 2; + + /*! minimal detectable step time */ + uint16_t steptime_min : 3; + + /*! enable step counter mode setting */ + uint16_t step_detector_mode : 2; + + /*! minimum step buffer size*/ + uint16_t step_min_buf : 3; +#else + + /*! minimum step buffer size*/ + uint16_t step_min_buf : 3; + + /*! enable step counter mode setting */ + uint16_t step_detector_mode : 2; + + /*! minimal detectable step time */ + uint16_t steptime_min : 3; + + /*! minimum threshold */ + uint16_t min_threshold : 2; + + /*! 1- step detector enable, 0- step detector disable */ + uint16_t step_detector_en : 1; +#endif +}; +struct bmi160_acc_no_motion_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! no motion interrupt x */ + uint16_t no_motion_x : 1; + + /*! no motion interrupt y */ + uint16_t no_motion_y : 1; + + /*! no motion interrupt z */ + uint16_t no_motion_z : 1; + + /*! no motion duration */ + uint16_t no_motion_dur : 6; + + /*! no motion sel , 1 - enable no-motion ,0- enable slow-motion */ + uint16_t no_motion_sel : 1; + + /*! data source 0- filter & 1 pre-filter*/ + uint16_t no_motion_src : 1; + + /*! no motion threshold */ + uint8_t no_motion_thres; +#else + + /*! no motion threshold */ + uint8_t no_motion_thres; + + /*! data source 0- filter & 1 pre-filter*/ + uint16_t no_motion_src : 1; + + /*! no motion sel , 1 - enable no-motion ,0- enable slow-motion */ + uint16_t no_motion_sel : 1; + + /*! no motion duration */ + uint16_t no_motion_dur : 6; + + /* no motion interrupt z */ + uint16_t no_motion_z : 1; + + /*! no motion interrupt y */ + uint16_t no_motion_y : 1; + + /*! no motion interrupt x */ + uint16_t no_motion_x : 1; +#endif +}; +struct bmi160_acc_orient_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! thresholds for switching between the different orientations */ + uint16_t orient_mode : 2; + + /*! blocking_mode */ + uint16_t orient_blocking : 2; + + /*! Orientation interrupt hysteresis */ + uint16_t orient_hyst : 4; + + /*! Orientation interrupt theta */ + uint16_t orient_theta : 6; + + /*! Enable/disable Orientation interrupt */ + uint16_t orient_ud_en : 1; + + /*! exchange x- and z-axis in algorithm ,0 - z, 1 - x */ + uint16_t axes_ex : 1; + + /*! 1 - orient enable, 0 - orient disable */ + uint8_t orient_en : 1; +#else + + /*! 1 - orient enable, 0 - orient disable */ + uint8_t orient_en : 1; + + /*! exchange x- and z-axis in algorithm ,0 - z, 1 - x */ + uint16_t axes_ex : 1; + + /*! Enable/disable Orientation interrupt */ + uint16_t orient_ud_en : 1; + + /*! Orientation interrupt theta */ + uint16_t orient_theta : 6; + + /*! Orientation interrupt hysteresis */ + uint16_t orient_hyst : 4; + + /*! blocking_mode */ + uint16_t orient_blocking : 2; + + /*! thresholds for switching between the different orientations */ + uint16_t orient_mode : 2; +#endif +}; +struct bmi160_acc_flat_detect_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! flat threshold */ + uint16_t flat_theta : 6; + + /*! flat interrupt hysteresis */ + uint16_t flat_hy : 3; + + /*! delay time for which the flat value must remain stable for the + * flat interrupt to be generated */ + uint16_t flat_hold_time : 2; + + /*! 1 - flat enable, 0 - flat disable */ + uint16_t flat_en : 1; +#else + + /*! 1 - flat enable, 0 - flat disable */ + uint16_t flat_en : 1; + + /*! delay time for which the flat value must remain stable for the + * flat interrupt to be generated */ + uint16_t flat_hold_time : 2; + + /*! flat interrupt hysteresis */ + uint16_t flat_hy : 3; + + /*! flat threshold */ + uint16_t flat_theta : 6; +#endif +}; +struct bmi160_acc_low_g_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! low-g interrupt trigger delay */ + uint8_t low_dur; + + /*! low-g interrupt trigger threshold */ + uint8_t low_thres; + + /*! hysteresis of low-g interrupt */ + uint8_t low_hyst : 2; + + /*! 0 - single-axis mode ,1 - axis-summing mode */ + uint8_t low_mode : 1; + + /*! data source 0- filter & 1 pre-filter */ + uint8_t low_data_src : 1; + + /*! 1 - enable low-g, 0 - disable low-g */ + uint8_t low_en : 1; +#else + + /*! 1 - enable low-g, 0 - disable low-g */ + uint8_t low_en : 1; + + /*! data source 0- filter & 1 pre-filter */ + uint8_t low_data_src : 1; + + /*! 0 - single-axis mode ,1 - axis-summing mode */ + uint8_t low_mode : 1; + + /*! hysteresis of low-g interrupt */ + uint8_t low_hyst : 2; + + /*! low-g interrupt trigger threshold */ + uint8_t low_thres; + + /*! low-g interrupt trigger delay */ + uint8_t low_dur; +#endif +}; +struct bmi160_acc_high_g_int_cfg { +#ifdef LITTLE_ENDIAN + + /*! High-g interrupt x, 1 - enable, 0 - disable */ + uint8_t high_g_x : 1; + + /*! High-g interrupt y, 1 - enable, 0 - disable */ + uint8_t high_g_y : 1; + + /*! High-g interrupt z, 1 - enable, 0 - disable */ + uint8_t high_g_z : 1; + + /*! High-g hysteresis */ + uint8_t high_hy : 2; + + /*! data source 0- filter & 1 pre-filter */ + uint8_t high_data_src : 1; + + /*! High-g threshold */ + uint8_t high_thres; + + /*! High-g duration */ + uint8_t high_dur; +#else + + /*! High-g duration */ + uint8_t high_dur; + + /*! High-g threshold */ + uint8_t high_thres; + + /*! data source 0- filter & 1 pre-filter */ + uint8_t high_data_src : 1; + + /*! High-g hysteresis */ + uint8_t high_hy : 2; + + /*! High-g interrupt z, 1 - enable, 0 - disable */ + uint8_t high_g_z : 1; + + /*! High-g interrupt y, 1 - enable, 0 - disable */ + uint8_t high_g_y : 1; + + /*! High-g interrupt x, 1 - enable, 0 - disable */ + uint8_t high_g_x : 1; +#endif +}; +struct bmi160_int_pin_settg { +#ifdef LITTLE_ENDIAN + + /*! To enable either INT1 or INT2 pin as output. + * 0- output disabled ,1- output enabled */ + uint16_t output_en : 1; + + /*! 0 - push-pull 1- open drain,only valid if output_en is set 1 */ + uint16_t output_mode : 1; + + /*! 0 - active low , 1 - active high level. + * if output_en is 1,this applies to interrupts,else PMU_trigger */ + uint16_t output_type : 1; + + /*! 0 - level trigger , 1 - edge trigger */ + uint16_t edge_ctrl : 1; + + /*! To enable either INT1 or INT2 pin as input. + * 0 - input disabled ,1 - input enabled */ + uint16_t input_en : 1; + + /*! latch duration*/ + uint16_t latch_dur : 4; +#else + + /*! latch duration*/ + uint16_t latch_dur : 4; + + /*! Latched,non-latched or temporary interrupt modes */ + uint16_t input_en : 1; + + /*! 1 - edge trigger, 0 - level trigger */ + uint16_t edge_ctrl : 1; + + /*! 0 - active low , 1 - active high level. + * if output_en is 1,this applies to interrupts,else PMU_trigger */ + uint16_t output_type : 1; + + /*! 0 - push-pull , 1 - open drain,only valid if output_en is set 1 */ + uint16_t output_mode : 1; + + /*! To enable either INT1 or INT2 pin as output. + * 0 - output disabled , 1 - output enabled */ + uint16_t output_en : 1; +#endif +}; +union bmi160_int_type_cfg { + /*! Tap interrupt structure */ + struct bmi160_acc_tap_int_cfg acc_tap_int; + + /*! Slope interrupt structure */ + struct bmi160_acc_any_mot_int_cfg acc_any_motion_int; + + /*! Significant motion interrupt structure */ + struct bmi160_acc_sig_mot_int_cfg acc_sig_motion_int; + + /*! Step detector interrupt structure */ + struct bmi160_acc_step_detect_int_cfg acc_step_detect_int; + + /*! No motion interrupt structure */ + struct bmi160_acc_no_motion_int_cfg acc_no_motion_int; + + /*! Orientation interrupt structure */ + struct bmi160_acc_orient_int_cfg acc_orient_int; + + /*! Flat interrupt structure */ + struct bmi160_acc_flat_detect_int_cfg acc_flat_int; + + /*! Low-g interrupt structure */ + struct bmi160_acc_low_g_int_cfg acc_low_g_int; + + /*! High-g interrupt structure */ + struct bmi160_acc_high_g_int_cfg acc_high_g_int; +}; +struct bmi160_int_settg { + /*! Interrupt channel */ + enum bmi160_int_channel int_channel; + + /*! Select Interrupt */ + enum bmi160_int_types int_type; + + /*! Structure configuring Interrupt pins */ + struct bmi160_int_pin_settg int_pin_settg; + + /*! Union configures required interrupt */ + union bmi160_int_type_cfg int_type_cfg; + + /*! FIFO FULL INT 1-enable, 0-disable */ + uint8_t fifo_full_int_en : 1; + + /*! FIFO WTM INT 1-enable, 0-disable */ + uint8_t fifo_wtm_int_en : 1; +}; + +/*! + * @brief This structure holds the information for usage of + * FIFO by the user. + */ +struct bmi160_fifo_frame { + /*! Data buffer of user defined length is to be mapped here */ + uint8_t* data; + + /*! While calling the API "bmi160_get_fifo_data" , length stores + * number of bytes in FIFO to be read (specified by user as input) + * and after execution of the API ,number of FIFO data bytes + * available is provided as an output to user + */ + uint16_t length; + + /*! FIFO time enable */ + uint8_t fifo_time_enable; + + /*! Enabling of the FIFO header to stream in header mode */ + uint8_t fifo_header_enable; + + /*! Streaming of the Accelerometer, Gyroscope + * sensor data or both in FIFO */ + uint8_t fifo_data_enable; + + /*! Will be equal to length when no more frames are there to parse */ + uint16_t accel_byte_start_idx; + + /*! Will be equal to length when no more frames are there to parse */ + uint16_t gyro_byte_start_idx; + + /*! Will be equal to length when no more frames are there to parse */ + uint16_t aux_byte_start_idx; + + /*! Value of FIFO sensor time time */ + uint32_t sensor_time; + + /*! Value of Skipped frame counts */ + uint8_t skipped_frame_count; +}; +struct bmi160_dev { + /*! Chip Id */ + uint8_t chip_id; + + /*! Device Id */ + uint8_t id; + + /*! 0 - I2C , 1 - SPI Interface */ + uint8_t intf; + + /*! Hold active interrupts status for any and sig motion + * 0 - Any-motion enable, 1 - Sig-motion enable, + * -1 neither any-motion nor sig-motion selected */ + enum bmi160_any_sig_motion_active_interrupt_state any_sig_sel; + + /*! Structure to configure Accel sensor */ + struct bmi160_cfg accel_cfg; + + /*! Structure to hold previous/old accel config parameters. + * This is used at driver level to prevent overwriting of same + * data, hence user does not change it in the code */ + struct bmi160_cfg prev_accel_cfg; + + /*! Structure to configure Gyro sensor */ + struct bmi160_cfg gyro_cfg; + + /*! Structure to hold previous/old gyro config parameters. + * This is used at driver level to prevent overwriting of same + * data, hence user does not change it in the code */ + struct bmi160_cfg prev_gyro_cfg; + + /*! Structure to configure the auxiliary sensor */ + struct bmi160_aux_cfg aux_cfg; + + /*! Structure to hold previous/old aux config parameters. + * This is used at driver level to prevent overwriting of same + * data, hence user does not change it in the code */ + struct bmi160_aux_cfg prev_aux_cfg; + + /*! FIFO related configurations */ + struct bmi160_fifo_frame* fifo; + + /*! Read function pointer */ + bmi160_read_fptr_t read; + + /*! Write function pointer */ + bmi160_write_fptr_t write; + + /*! Delay function pointer */ + bmi160_delay_fptr_t delay_ms; + + /*! User set read/write length */ + uint16_t read_write_len; +}; + +#endif /* BMI160_DEFS_H_ */ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/imu.c b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/imu.c new file mode 100644 index 000000000..5e89c9504 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/imu.c @@ -0,0 +1,29 @@ +#include "imu.h" +#include + +bool bmi160_begin(); +int bmi160_read(double* vec); + +bool lsm6ds3trc_begin(); +void lsm6ds3trc_end(); +int lsm6ds3trc_read(double* vec); + +bool imu_begin() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + bool ret = bmi160_begin(); // lsm6ds3trc_begin(); + furi_hal_i2c_release(&furi_hal_i2c_handle_external); + return ret; +} + +void imu_end() { + // furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + // lsm6ds3trc_end(); + // furi_hal_i2c_release(&furi_hal_i2c_handle_external); +} + +int imu_read(double* vec) { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + int ret = bmi160_read(vec); // lsm6ds3trc_read(vec); + furi_hal_i2c_release(&furi_hal_i2c_handle_external); + return ret; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/imu.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/imu.h new file mode 100644 index 000000000..f4c5e4b1d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/imu.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ACC_DATA_READY (1 << 0) +#define GYR_DATA_READY (1 << 1) + +bool imu_begin(); +void imu_end(); +int imu_read(double* vec); + +#ifdef __cplusplus +} +#endif diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/imu_bmi160.c b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/imu_bmi160.c new file mode 100644 index 000000000..af771302f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/imu_bmi160.c @@ -0,0 +1,88 @@ +#include "bmi160.h" + +#include + +#include "imu.h" + +#define TAG "BMI160" + +#define BMI160_DEV_ADDR (0x69 << 1) + +static const double DEG_TO_RAD = 0.017453292519943295769236907684886; +static const double G = 9.81; + +struct bmi160_dev bmi160dev; +struct bmi160_sensor_data bmi160_accel; +struct bmi160_sensor_data bmi160_gyro; + +int8_t bmi160_write_i2c(uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint16_t len) { + if(furi_hal_i2c_write_mem(&furi_hal_i2c_handle_external, dev_addr, reg_addr, data, len, 50)) + return BMI160_OK; + return BMI160_E_COM_FAIL; +} + +int8_t bmi160_read_i2c(uint8_t dev_addr, uint8_t reg_addr, uint8_t* read_data, uint16_t len) { + if(furi_hal_i2c_read_mem(&furi_hal_i2c_handle_external, dev_addr, reg_addr, read_data, len, 50)) + return BMI160_OK; + return BMI160_E_COM_FAIL; +} + +bool bmi160_begin() { + FURI_LOG_I(TAG, "Init BMI160"); + + if(!furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, BMI160_DEV_ADDR, 50)) { + FURI_LOG_E(TAG, "Device not ready!"); + return false; + } + + FURI_LOG_I(TAG, "Device ready!"); + + bmi160dev.id = BMI160_DEV_ADDR; + bmi160dev.intf = BMI160_I2C_INTF; + bmi160dev.read = bmi160_read_i2c; + bmi160dev.write = bmi160_write_i2c; + bmi160dev.delay_ms = furi_delay_ms; + + if(bmi160_init(&bmi160dev) != BMI160_OK) { + FURI_LOG_E(TAG, "Initialization failure!"); + FURI_LOG_E(TAG, "Chip ID 0x%X", bmi160dev.chip_id); + return false; + } + + bmi160dev.accel_cfg.odr = BMI160_ACCEL_ODR_400HZ; + bmi160dev.accel_cfg.range = BMI160_ACCEL_RANGE_4G; + bmi160dev.accel_cfg.bw = BMI160_ACCEL_BW_NORMAL_AVG4; + bmi160dev.accel_cfg.power = BMI160_ACCEL_NORMAL_MODE; + bmi160dev.gyro_cfg.odr = BMI160_GYRO_ODR_400HZ; + bmi160dev.gyro_cfg.range = BMI160_GYRO_RANGE_2000_DPS; + bmi160dev.gyro_cfg.bw = BMI160_GYRO_BW_NORMAL_MODE; + bmi160dev.gyro_cfg.power = BMI160_GYRO_NORMAL_MODE; + + if(bmi160_set_sens_conf(&bmi160dev) != BMI160_OK) { + FURI_LOG_E(TAG, "Initialization failure!"); + FURI_LOG_E(TAG, "Chip ID 0x%X", bmi160dev.chip_id); + return false; + } + + FURI_LOG_I(TAG, "Initialization success!"); + FURI_LOG_I(TAG, "Chip ID 0x%X", bmi160dev.chip_id); + + return true; +} + +int bmi160_read(double* vec) { + if(bmi160_get_sensor_data( + (BMI160_ACCEL_SEL | BMI160_GYRO_SEL), &bmi160_accel, &bmi160_gyro, &bmi160dev) != + BMI160_OK) { + return 0; + } + + vec[0] = ((double)bmi160_accel.x * 4 / 32768) * G; + vec[1] = ((double)bmi160_accel.y * 4 / 32768) * G; + vec[2] = ((double)bmi160_accel.z * 4 / 32768) * G; + vec[3] = ((double)bmi160_gyro.x * 2000 / 32768) * DEG_TO_RAD; + vec[4] = ((double)bmi160_gyro.y * 2000 / 32768) * DEG_TO_RAD; + vec[5] = ((double)bmi160_gyro.z * 2000 / 32768) * DEG_TO_RAD; + + return ACC_DATA_READY | GYR_DATA_READY; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/imu_lsm6ds3trc.c b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/imu_lsm6ds3trc.c new file mode 100644 index 000000000..c013fc6e6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/imu_lsm6ds3trc.c @@ -0,0 +1,94 @@ +#include "lsm6ds3tr_c_reg.h" + +#include + +#include "imu.h" + +#define TAG "LSM6DS3TR-C" + +#define LSM6DS3_ADDRESS (0x6A << 1) + +static const double DEG_TO_RAD = 0.017453292519943295769236907684886; + +stmdev_ctx_t lsm6ds3trc_ctx; + +int32_t lsm6ds3trc_write_i2c(void* handle, uint8_t reg_addr, const uint8_t* data, uint16_t len) { + if(furi_hal_i2c_write_mem(handle, LSM6DS3_ADDRESS, reg_addr, (uint8_t*)data, len, 50)) + return 0; + return -1; +} + +int32_t lsm6ds3trc_read_i2c(void* handle, uint8_t reg_addr, uint8_t* read_data, uint16_t len) { + if(furi_hal_i2c_read_mem(handle, LSM6DS3_ADDRESS, reg_addr, read_data, len, 50)) return 0; + return -1; +} + +bool lsm6ds3trc_begin() { + FURI_LOG_I(TAG, "Init LSM6DS3TR-C"); + + if(!furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, LSM6DS3_ADDRESS, 50)) { + FURI_LOG_E(TAG, "Not ready"); + return false; + } + + lsm6ds3trc_ctx.write_reg = lsm6ds3trc_write_i2c; + lsm6ds3trc_ctx.read_reg = lsm6ds3trc_read_i2c; + lsm6ds3trc_ctx.mdelay = furi_delay_ms; + lsm6ds3trc_ctx.handle = &furi_hal_i2c_handle_external; + + uint8_t whoami; + lsm6ds3tr_c_device_id_get(&lsm6ds3trc_ctx, &whoami); + if(whoami != LSM6DS3TR_C_ID) { + FURI_LOG_I(TAG, "Unknown model: %x", (int)whoami); + return false; + } + + lsm6ds3tr_c_reset_set(&lsm6ds3trc_ctx, PROPERTY_ENABLE); + uint8_t rst = PROPERTY_ENABLE; + while(rst) lsm6ds3tr_c_reset_get(&lsm6ds3trc_ctx, &rst); + + lsm6ds3tr_c_block_data_update_set(&lsm6ds3trc_ctx, PROPERTY_ENABLE); + lsm6ds3tr_c_fifo_mode_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_BYPASS_MODE); + + lsm6ds3tr_c_xl_data_rate_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_XL_ODR_104Hz); + lsm6ds3tr_c_xl_full_scale_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_4g); + lsm6ds3tr_c_xl_lp1_bandwidth_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_XL_LP1_ODR_DIV_4); + + lsm6ds3tr_c_gy_data_rate_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_GY_ODR_104Hz); + lsm6ds3tr_c_gy_full_scale_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_2000dps); + lsm6ds3tr_c_gy_power_mode_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_GY_HIGH_PERFORMANCE); + lsm6ds3tr_c_gy_band_pass_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_LP2_ONLY); + + FURI_LOG_I(TAG, "Init OK"); + return true; +} + +void lsm6ds3trc_end() { + lsm6ds3tr_c_xl_data_rate_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_XL_ODR_OFF); + lsm6ds3tr_c_gy_data_rate_set(&lsm6ds3trc_ctx, LSM6DS3TR_C_GY_ODR_OFF); +} + +int lsm6ds3trc_read(double* vec) { + int ret = 0; + int16_t data[3]; + lsm6ds3tr_c_reg_t reg; + lsm6ds3tr_c_status_reg_get(&lsm6ds3trc_ctx, ®.status_reg); + + if(reg.status_reg.xlda) { + lsm6ds3tr_c_acceleration_raw_get(&lsm6ds3trc_ctx, data); + vec[2] = (double)lsm6ds3tr_c_from_fs2g_to_mg(data[0]) / 1000; + vec[0] = (double)lsm6ds3tr_c_from_fs2g_to_mg(data[1]) / 1000; + vec[1] = (double)lsm6ds3tr_c_from_fs2g_to_mg(data[2]) / 1000; + ret |= ACC_DATA_READY; + } + + if(reg.status_reg.gda) { + lsm6ds3tr_c_angular_rate_raw_get(&lsm6ds3trc_ctx, data); + vec[5] = (double)lsm6ds3tr_c_from_fs2000dps_to_mdps(data[0]) * DEG_TO_RAD / 1000; + vec[3] = (double)lsm6ds3tr_c_from_fs2000dps_to_mdps(data[1]) * DEG_TO_RAD / 1000; + vec[4] = (double)lsm6ds3tr_c_from_fs2000dps_to_mdps(data[2]) * DEG_TO_RAD / 1000; + ret |= GYR_DATA_READY; + } + + return ret; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/lsm6ds3tr_c_reg.c b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/lsm6ds3tr_c_reg.c new file mode 100644 index 000000000..9f1890d2c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/lsm6ds3tr_c_reg.c @@ -0,0 +1,7105 @@ +/** + ****************************************************************************** + * @file lsm6ds3tr_c_reg.c + * @author Sensors Software Solution Team + * @brief LSM6DS3TR_C driver file + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2021 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +#include "lsm6ds3tr_c_reg.h" + +/** + * @defgroup LSM6DS3TR_C + * @brief This file provides a set of functions needed to drive the + * lsm6ds3tr_c enanced inertial module. + * @{ + * + */ + +/** + * @defgroup LSM6DS3TR_C_interfaces_functions + * @brief This section provide a set of functions used to read and + * write a generic register of the device. + * MANDATORY: return 0 -> no Error. + * @{ + * + */ + +/** + * @brief Read generic device register + * + * @param ctx read / write interface definitions(ptr) + * @param reg register to read + * @param data pointer to buffer that store the data read(ptr) + * @param len number of consecutive register to read + * @retval interface status (MANDATORY: return 0 -> no Error) + * + */ +int32_t lsm6ds3tr_c_read_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, uint16_t len) { + int32_t ret; + + ret = ctx->read_reg(ctx->handle, reg, data, len); + + return ret; +} + +/** + * @brief Write generic device register + * + * @param ctx read / write interface definitions(ptr) + * @param reg register to write + * @param data pointer to data to write in register reg(ptr) + * @param len number of consecutive register to write + * @retval interface status (MANDATORY: return 0 -> no Error) + * + */ +int32_t lsm6ds3tr_c_write_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, uint16_t len) { + int32_t ret; + + ret = ctx->write_reg(ctx->handle, reg, data, len); + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Sensitivity + * @brief These functions convert raw-data into engineering units. + * @{ + * + */ + +float_t lsm6ds3tr_c_from_fs2g_to_mg(int16_t lsb) { + return ((float_t)lsb * 0.061f); +} + +float_t lsm6ds3tr_c_from_fs4g_to_mg(int16_t lsb) { + return ((float_t)lsb * 0.122f); +} + +float_t lsm6ds3tr_c_from_fs8g_to_mg(int16_t lsb) { + return ((float_t)lsb * 0.244f); +} + +float_t lsm6ds3tr_c_from_fs16g_to_mg(int16_t lsb) { + return ((float_t)lsb * 0.488f); +} + +float_t lsm6ds3tr_c_from_fs125dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 4.375f); +} + +float_t lsm6ds3tr_c_from_fs250dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 8.750f); +} + +float_t lsm6ds3tr_c_from_fs500dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 17.50f); +} + +float_t lsm6ds3tr_c_from_fs1000dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 35.0f); +} + +float_t lsm6ds3tr_c_from_fs2000dps_to_mdps(int16_t lsb) { + return ((float_t)lsb * 70.0f); +} + +float_t lsm6ds3tr_c_from_lsb_to_celsius(int16_t lsb) { + return (((float_t)lsb / 256.0f) + 25.0f); +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_data_generation + * @brief This section groups all the functions concerning data + * generation + * @{ + * + */ + +/** + * @brief Accelerometer full-scale selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fs_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_xl_t val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ctrl1_xl.fs_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + } + + return ret; +} + +/** + * @brief Accelerometer full-scale selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of fs_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_xl_t* val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + switch(ctrl1_xl.fs_xl) { + case LSM6DS3TR_C_2g: + *val = LSM6DS3TR_C_2g; + break; + + case LSM6DS3TR_C_16g: + *val = LSM6DS3TR_C_16g; + break; + + case LSM6DS3TR_C_4g: + *val = LSM6DS3TR_C_4g; + break; + + case LSM6DS3TR_C_8g: + *val = LSM6DS3TR_C_8g; + break; + + default: + *val = LSM6DS3TR_C_XL_FS_ND; + break; + } + + return ret; +} + +/** + * @brief Accelerometer data rate selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of odr_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_xl_t val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ctrl1_xl.odr_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + } + + return ret; +} + +/** + * @brief Accelerometer data rate selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of odr_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_xl_t* val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + switch(ctrl1_xl.odr_xl) { + case LSM6DS3TR_C_XL_ODR_OFF: + *val = LSM6DS3TR_C_XL_ODR_OFF; + break; + + case LSM6DS3TR_C_XL_ODR_12Hz5: + *val = LSM6DS3TR_C_XL_ODR_12Hz5; + break; + + case LSM6DS3TR_C_XL_ODR_26Hz: + *val = LSM6DS3TR_C_XL_ODR_26Hz; + break; + + case LSM6DS3TR_C_XL_ODR_52Hz: + *val = LSM6DS3TR_C_XL_ODR_52Hz; + break; + + case LSM6DS3TR_C_XL_ODR_104Hz: + *val = LSM6DS3TR_C_XL_ODR_104Hz; + break; + + case LSM6DS3TR_C_XL_ODR_208Hz: + *val = LSM6DS3TR_C_XL_ODR_208Hz; + break; + + case LSM6DS3TR_C_XL_ODR_416Hz: + *val = LSM6DS3TR_C_XL_ODR_416Hz; + break; + + case LSM6DS3TR_C_XL_ODR_833Hz: + *val = LSM6DS3TR_C_XL_ODR_833Hz; + break; + + case LSM6DS3TR_C_XL_ODR_1k66Hz: + *val = LSM6DS3TR_C_XL_ODR_1k66Hz; + break; + + case LSM6DS3TR_C_XL_ODR_3k33Hz: + *val = LSM6DS3TR_C_XL_ODR_3k33Hz; + break; + + case LSM6DS3TR_C_XL_ODR_6k66Hz: + *val = LSM6DS3TR_C_XL_ODR_6k66Hz; + break; + + case LSM6DS3TR_C_XL_ODR_1Hz6: + *val = LSM6DS3TR_C_XL_ODR_1Hz6; + break; + + default: + *val = LSM6DS3TR_C_XL_ODR_ND; + break; + } + + return ret; +} + +/** + * @brief Gyroscope chain full-scale selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fs_g in reg CTRL2_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_g_t val) { + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + + if(ret == 0) { + ctrl2_g.fs_g = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + } + + return ret; +} + +/** + * @brief Gyroscope chain full-scale selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of fs_g in reg CTRL2_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_g_t* val) { + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + + switch(ctrl2_g.fs_g) { + case LSM6DS3TR_C_250dps: + *val = LSM6DS3TR_C_250dps; + break; + + case LSM6DS3TR_C_125dps: + *val = LSM6DS3TR_C_125dps; + break; + + case LSM6DS3TR_C_500dps: + *val = LSM6DS3TR_C_500dps; + break; + + case LSM6DS3TR_C_1000dps: + *val = LSM6DS3TR_C_1000dps; + break; + + case LSM6DS3TR_C_2000dps: + *val = LSM6DS3TR_C_2000dps; + break; + + default: + *val = LSM6DS3TR_C_GY_FS_ND; + break; + } + + return ret; +} + +/** + * @brief Gyroscope data rate selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of odr_g in reg CTRL2_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_g_t val) { + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + + if(ret == 0) { + ctrl2_g.odr_g = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + } + + return ret; +} + +/** + * @brief Gyroscope data rate selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of odr_g in reg CTRL2_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_g_t* val) { + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL2_G, (uint8_t*)&ctrl2_g, 1); + + switch(ctrl2_g.odr_g) { + case LSM6DS3TR_C_GY_ODR_OFF: + *val = LSM6DS3TR_C_GY_ODR_OFF; + break; + + case LSM6DS3TR_C_GY_ODR_12Hz5: + *val = LSM6DS3TR_C_GY_ODR_12Hz5; + break; + + case LSM6DS3TR_C_GY_ODR_26Hz: + *val = LSM6DS3TR_C_GY_ODR_26Hz; + break; + + case LSM6DS3TR_C_GY_ODR_52Hz: + *val = LSM6DS3TR_C_GY_ODR_52Hz; + break; + + case LSM6DS3TR_C_GY_ODR_104Hz: + *val = LSM6DS3TR_C_GY_ODR_104Hz; + break; + + case LSM6DS3TR_C_GY_ODR_208Hz: + *val = LSM6DS3TR_C_GY_ODR_208Hz; + break; + + case LSM6DS3TR_C_GY_ODR_416Hz: + *val = LSM6DS3TR_C_GY_ODR_416Hz; + break; + + case LSM6DS3TR_C_GY_ODR_833Hz: + *val = LSM6DS3TR_C_GY_ODR_833Hz; + break; + + case LSM6DS3TR_C_GY_ODR_1k66Hz: + *val = LSM6DS3TR_C_GY_ODR_1k66Hz; + break; + + case LSM6DS3TR_C_GY_ODR_3k33Hz: + *val = LSM6DS3TR_C_GY_ODR_3k33Hz; + break; + + case LSM6DS3TR_C_GY_ODR_6k66Hz: + *val = LSM6DS3TR_C_GY_ODR_6k66Hz; + break; + + default: + *val = LSM6DS3TR_C_GY_ODR_ND; + break; + } + + return ret; +} + +/** + * @brief Block data update.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of bdu in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_block_data_update_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.bdu = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Block data update.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of bdu in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_block_data_update_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + *val = ctrl3_c.bdu; + + return ret; +} + +/** + * @brief Weight of XL user offset bits of registers + * X_OFS_USR(73h), Y_OFS_USR(74h), Z_OFS_USR(75h).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of usr_off_w in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_offset_weight_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_usr_off_w_t val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ctrl6_c.usr_off_w = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + } + + return ret; +} + +/** + * @brief Weight of XL user offset bits of registers + * X_OFS_USR(73h), Y_OFS_USR(74h), Z_OFS_USR(75h).[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of usr_off_w in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_offset_weight_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_usr_off_w_t* val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + switch(ctrl6_c.usr_off_w) { + case LSM6DS3TR_C_LSb_1mg: + *val = LSM6DS3TR_C_LSb_1mg; + break; + + case LSM6DS3TR_C_LSb_16mg: + *val = LSM6DS3TR_C_LSb_16mg; + break; + + default: + *val = LSM6DS3TR_C_WEIGHT_ND; + break; + } + + return ret; +} + +/** + * @brief High-performance operating mode for accelerometer[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of xl_hm_mode in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_power_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_xl_hm_mode_t val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ctrl6_c.xl_hm_mode = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + } + + return ret; +} + +/** + * @brief High-performance operating mode for accelerometer.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of xl_hm_mode in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_power_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_xl_hm_mode_t* val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + switch(ctrl6_c.xl_hm_mode) { + case LSM6DS3TR_C_XL_HIGH_PERFORMANCE: + *val = LSM6DS3TR_C_XL_HIGH_PERFORMANCE; + break; + + case LSM6DS3TR_C_XL_NORMAL: + *val = LSM6DS3TR_C_XL_NORMAL; + break; + + default: + *val = LSM6DS3TR_C_XL_PW_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Source register rounding function on WAKE_UP_SRC (1Bh), + * TAP_SRC (1Ch), D6D_SRC (1Dh), STATUS_REG (1Eh) and + * FUNC_SRC1 (53h) registers in the primary interface.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of rounding_status in reg CTRL7_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_rounding_on_status_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_status_t val) { + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + if(ret == 0) { + ctrl7_g.rounding_status = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + } + + return ret; +} + +/** + * @brief Source register rounding function on WAKE_UP_SRC (1Bh), + * TAP_SRC (1Ch), D6D_SRC (1Dh), STATUS_REG (1Eh) and + * FUNC_SRC1 (53h) registers in the primary interface.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of rounding_status in reg CTRL7_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_rounding_on_status_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_status_t* val) { + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + switch(ctrl7_g.rounding_status) { + case LSM6DS3TR_C_STAT_RND_DISABLE: + *val = LSM6DS3TR_C_STAT_RND_DISABLE; + break; + + case LSM6DS3TR_C_STAT_RND_ENABLE: + *val = LSM6DS3TR_C_STAT_RND_ENABLE; + break; + + default: + *val = LSM6DS3TR_C_STAT_RND_ND; + break; + } + + return ret; +} + +/** + * @brief High-performance operating mode disable for gyroscope.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of g_hm_mode in reg CTRL7_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_power_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_g_hm_mode_t val) { + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + if(ret == 0) { + ctrl7_g.g_hm_mode = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + } + + return ret; +} + +/** + * @brief High-performance operating mode disable for gyroscope.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of g_hm_mode in reg CTRL7_G + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_power_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_g_hm_mode_t* val) { + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + switch(ctrl7_g.g_hm_mode) { + case LSM6DS3TR_C_GY_HIGH_PERFORMANCE: + *val = LSM6DS3TR_C_GY_HIGH_PERFORMANCE; + break; + + case LSM6DS3TR_C_GY_NORMAL: + *val = LSM6DS3TR_C_GY_NORMAL; + break; + + default: + *val = LSM6DS3TR_C_GY_PW_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Read all the interrupt/status flag of the device.[get] + * + * @param ctx Read / write interface definitions + * @param val WAKE_UP_SRC, TAP_SRC, D6D_SRC, STATUS_REG, + * FUNC_SRC1, FUNC_SRC2, WRIST_TILT_IA, A_WRIST_TILT_Mask + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_all_sources_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_all_sources_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_SRC, (uint8_t*)&(val->wake_up_src), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_SRC, (uint8_t*)&(val->tap_src), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_D6D_SRC, (uint8_t*)&(val->d6d_src), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)&(val->status_reg), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FUNC_SRC1, (uint8_t*)&(val->func_src1), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FUNC_SRC2, (uint8_t*)&(val->func_src2), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_WRIST_TILT_IA, (uint8_t*)&(val->wrist_tilt_ia), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_A_WRIST_TILT_MASK, (uint8_t*)&(val->a_wrist_tilt_mask), 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + + return ret; +} +/** + * @brief The STATUS_REG register is read by the primary interface[get] + * + * @param ctx Read / write interface definitions + * @param val Registers STATUS_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_status_reg_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_status_reg_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)val, 1); + + return ret; +} + +/** + * @brief Accelerometer new data available.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of xlda in reg STATUS_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_status_reg_t status_reg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)&status_reg, 1); + *val = status_reg.xlda; + + return ret; +} + +/** + * @brief Gyroscope new data available.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of gda in reg STATUS_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_status_reg_t status_reg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)&status_reg, 1); + *val = status_reg.gda; + + return ret; +} + +/** + * @brief Temperature new data available.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tda in reg STATUS_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_temp_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_status_reg_t status_reg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STATUS_REG, (uint8_t*)&status_reg, 1); + *val = status_reg.tda; + + return ret; +} + +/** + * @brief Accelerometer axis user offset correction expressed in two’s + * complement, weight depends on USR_OFF_W in CTRL6_C. + * The value must be in the range [-127 127].[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_usr_offset_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_X_OFS_USR, buff, 3); + + return ret; +} + +/** + * @brief Accelerometer axis user offset correction xpressed in two’s + * complement, weight depends on USR_OFF_W in CTRL6_C. + * The value must be in the range [-127 127].[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_usr_offset_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_X_OFS_USR, buff, 3); + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Timestamp + * @brief This section groups all the functions that manage the + * timestamp generation. + * @{ + * + */ + +/** + * @brief Enable timestamp count. The count is saved in TIMESTAMP0_REG (40h), + * TIMESTAMP1_REG (41h) and TIMESTAMP2_REG (42h).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_timestamp_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.timer_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + } + + return ret; +} + +/** + * @brief Enable timestamp count. The count is saved in TIMESTAMP0_REG (40h), + * TIMESTAMP1_REG (41h) and TIMESTAMP2_REG (42h).[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_timestamp_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.timer_en; + + return ret; +} + +/** + * @brief Timestamp register resolution setting. + * Configuration of this bit affects + * TIMESTAMP0_REG(40h), TIMESTAMP1_REG(41h), + * TIMESTAMP2_REG(42h), STEP_TIMESTAMP_L(49h), + * STEP_TIMESTAMP_H(4Ah) and + * STEP_COUNT_DELTA(15h) registers.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_hr in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_timestamp_res_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_timer_hr_t val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + wake_up_dur.timer_hr = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + } + + return ret; +} + +/** + * @brief Timestamp register resolution setting. + * Configuration of this bit affects + * TIMESTAMP0_REG(40h), TIMESTAMP1_REG(41h), + * TIMESTAMP2_REG(42h), STEP_TIMESTAMP_L(49h), + * STEP_TIMESTAMP_H(4Ah) and + * STEP_COUNT_DELTA(15h) registers.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of timer_hr in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_timestamp_res_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_timer_hr_t* val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + switch(wake_up_dur.timer_hr) { + case LSM6DS3TR_C_LSB_6ms4: + *val = LSM6DS3TR_C_LSB_6ms4; + break; + + case LSM6DS3TR_C_LSB_25us: + *val = LSM6DS3TR_C_LSB_25us; + break; + + default: + *val = LSM6DS3TR_C_TS_RES_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Dataoutput + * @brief This section groups all the data output functions. + * @{ + * + */ + +/** + * @brief Circular burst-mode (rounding) read from output registers + * through the primary interface.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of rounding in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_rounding_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_t val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + if(ret == 0) { + ctrl5_c.rounding = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + } + + return ret; +} + +/** + * @brief Circular burst-mode (rounding) read from output registers + * through the primary interface.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of rounding in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_rounding_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_t* val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + switch(ctrl5_c.rounding) { + case LSM6DS3TR_C_ROUND_DISABLE: + *val = LSM6DS3TR_C_ROUND_DISABLE; + break; + + case LSM6DS3TR_C_ROUND_XL: + *val = LSM6DS3TR_C_ROUND_XL; + break; + + case LSM6DS3TR_C_ROUND_GY: + *val = LSM6DS3TR_C_ROUND_GY; + break; + + case LSM6DS3TR_C_ROUND_GY_XL: + *val = LSM6DS3TR_C_ROUND_GY_XL; + break; + + case LSM6DS3TR_C_ROUND_SH1_TO_SH6: + *val = LSM6DS3TR_C_ROUND_SH1_TO_SH6; + break; + + case LSM6DS3TR_C_ROUND_XL_SH1_TO_SH6: + *val = LSM6DS3TR_C_ROUND_XL_SH1_TO_SH6; + break; + + case LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH12: + *val = LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH12; + break; + + case LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH6: + *val = LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH6; + break; + + default: + *val = LSM6DS3TR_C_ROUND_OUT_ND; + break; + } + + return ret; +} + +/** + * @brief Temperature data output register (r). L and H registers together + * express a 16-bit word in two’s complement.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_temperature_raw_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[2]; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_OUT_TEMP_L, buff, 2); + *val = (int16_t)buff[1]; + *val = (*val * 256) + (int16_t)buff[0]; + + return ret; +} + +/** + * @brief Angular rate sensor. The value is expressed as a 16-bit word in + * two’s complement.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_angular_rate_raw_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_OUTX_L_G, buff, 6); + val[0] = (int16_t)buff[1]; + val[0] = (val[0] * 256) + (int16_t)buff[0]; + val[1] = (int16_t)buff[3]; + val[1] = (val[1] * 256) + (int16_t)buff[2]; + val[2] = (int16_t)buff[5]; + val[2] = (val[2] * 256) + (int16_t)buff[4]; + + return ret; +} + +/** + * @brief Linear acceleration output register. The value is expressed + * as a 16-bit word in two’s complement.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_acceleration_raw_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_OUTX_L_XL, buff, 6); + val[0] = (int16_t)buff[1]; + val[0] = (val[0] * 256) + (int16_t)buff[0]; + val[1] = (int16_t)buff[3]; + val[1] = (val[1] * 256) + (int16_t)buff[2]; + val[2] = (int16_t)buff[5]; + val[2] = (val[2] * 256) + (int16_t)buff[4]; + + return ret; +} + +/** + * @brief External magnetometer raw data.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_calibrated_raw_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_OUT_MAG_RAW_X_L, buff, 6); + val[0] = (int16_t)buff[1]; + val[0] = (val[0] * 256) + (int16_t)buff[0]; + val[1] = (int16_t)buff[3]; + val[1] = (val[1] * 256) + (int16_t)buff[2]; + val[2] = (int16_t)buff[5]; + val[2] = (val[2] * 256) + (int16_t)buff[4]; + + return ret; +} + +/** + * @brief Read data in FIFO.[get] + * + * @param ctx Read / write interface definitions + * @param buffer Data buffer to store FIFO data. + * @param len Number of data to read from FIFO. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_raw_data_get(stmdev_ctx_t* ctx, uint8_t* buffer, uint8_t len) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_DATA_OUT_L, buffer, len); + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_common + * @brief This section groups common useful functions. + * @{ + * + */ + +/** + * @brief Enable access to the embedded functions/sensor hub + * configuration registers[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of func_cfg_en in reg FUNC_CFG_ACCESS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mem_bank_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_func_cfg_en_t val) { + lsm6ds3tr_c_func_cfg_access_t func_cfg_access; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FUNC_CFG_ACCESS, (uint8_t*)&func_cfg_access, 1); + + if(ret == 0) { + func_cfg_access.func_cfg_en = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FUNC_CFG_ACCESS, (uint8_t*)&func_cfg_access, 1); + } + + return ret; +} + +/** + * @brief Enable access to the embedded functions/sensor hub configuration + * registers[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of func_cfg_en in reg FUNC_CFG_ACCESS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mem_bank_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_func_cfg_en_t* val) { + lsm6ds3tr_c_func_cfg_access_t func_cfg_access; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FUNC_CFG_ACCESS, (uint8_t*)&func_cfg_access, 1); + + switch(func_cfg_access.func_cfg_en) { + case LSM6DS3TR_C_USER_BANK: + *val = LSM6DS3TR_C_USER_BANK; + break; + + case LSM6DS3TR_C_BANK_B: + *val = LSM6DS3TR_C_BANK_B; + break; + + default: + *val = LSM6DS3TR_C_BANK_ND; + break; + } + + return ret; +} + +/** + * @brief Data-ready pulsed / letched mode[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_pulsed in reg DRDY_PULSE_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_data_ready_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_drdy_pulsed_g_t val) { + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + + if(ret == 0) { + drdy_pulse_cfg_g.drdy_pulsed = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + } + + return ret; +} + +/** + * @brief Data-ready pulsed / letched mode[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of drdy_pulsed in reg DRDY_PULSE_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_data_ready_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_drdy_pulsed_g_t* val) { + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + + switch(drdy_pulse_cfg_g.drdy_pulsed) { + case LSM6DS3TR_C_DRDY_LATCHED: + *val = LSM6DS3TR_C_DRDY_LATCHED; + break; + + case LSM6DS3TR_C_DRDY_PULSED: + *val = LSM6DS3TR_C_DRDY_PULSED; + break; + + default: + *val = LSM6DS3TR_C_DRDY_ND; + break; + } + + return ret; +} + +/** + * @brief DeviceWhoamI.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_device_id_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WHO_AM_I, buff, 1); + + return ret; +} + +/** + * @brief Software reset. Restore the default values in user registers[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sw_reset in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_reset_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.sw_reset = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Software reset. Restore the default values in user registers[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sw_reset in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_reset_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + *val = ctrl3_c.sw_reset; + + return ret; +} + +/** + * @brief Big/Little Endian Data selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ble in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_data_format_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_ble_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.ble = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Big/Little Endian Data selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of ble in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_data_format_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_ble_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + switch(ctrl3_c.ble) { + case LSM6DS3TR_C_LSB_AT_LOW_ADD: + *val = LSM6DS3TR_C_LSB_AT_LOW_ADD; + break; + + case LSM6DS3TR_C_MSB_AT_LOW_ADD: + *val = LSM6DS3TR_C_MSB_AT_LOW_ADD; + break; + + default: + *val = LSM6DS3TR_C_DATA_FMT_ND; + break; + } + + return ret; +} + +/** + * @brief Register address automatically incremented during a multiple byte + * access with a serial interface.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of if_inc in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_auto_increment_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.if_inc = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Register address automatically incremented during a multiple byte + * access with a serial interface.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of if_inc in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_auto_increment_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + *val = ctrl3_c.if_inc; + + return ret; +} + +/** + * @brief Reboot memory content. Reload the calibration parameters.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of boot in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_boot_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.boot = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Reboot memory content. Reload the calibration parameters.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of boot in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_boot_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + *val = ctrl3_c.boot; + + return ret; +} + +/** + * @brief Linear acceleration sensor self-test enable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of st_xl in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_self_test_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_xl_t val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + if(ret == 0) { + ctrl5_c.st_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + } + + return ret; +} + +/** + * @brief Linear acceleration sensor self-test enable.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of st_xl in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_self_test_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_xl_t* val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + switch(ctrl5_c.st_xl) { + case LSM6DS3TR_C_XL_ST_DISABLE: + *val = LSM6DS3TR_C_XL_ST_DISABLE; + break; + + case LSM6DS3TR_C_XL_ST_POSITIVE: + *val = LSM6DS3TR_C_XL_ST_POSITIVE; + break; + + case LSM6DS3TR_C_XL_ST_NEGATIVE: + *val = LSM6DS3TR_C_XL_ST_NEGATIVE; + break; + + default: + *val = LSM6DS3TR_C_XL_ST_ND; + break; + } + + return ret; +} + +/** + * @brief Angular rate sensor self-test enable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of st_g in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_self_test_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_g_t val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + if(ret == 0) { + ctrl5_c.st_g = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + } + + return ret; +} + +/** + * @brief Angular rate sensor self-test enable.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of st_g in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_self_test_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_g_t* val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + switch(ctrl5_c.st_g) { + case LSM6DS3TR_C_GY_ST_DISABLE: + *val = LSM6DS3TR_C_GY_ST_DISABLE; + break; + + case LSM6DS3TR_C_GY_ST_POSITIVE: + *val = LSM6DS3TR_C_GY_ST_POSITIVE; + break; + + case LSM6DS3TR_C_GY_ST_NEGATIVE: + *val = LSM6DS3TR_C_GY_ST_NEGATIVE; + break; + + default: + *val = LSM6DS3TR_C_GY_ST_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_filters + * @brief This section group all the functions concerning the filters + * configuration that impact both accelerometer and gyro. + * @{ + * + */ + +/** + * @brief Mask DRDY on pin (both XL & Gyro) until filter settling ends + * (XL and Gyro independently masked).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_mask in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_filter_settling_mask_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.drdy_mask = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + return ret; +} + +/** + * @brief Mask DRDY on pin (both XL & Gyro) until filter settling ends + * (XL and Gyro independently masked).[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_mask in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_filter_settling_mask_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + *val = ctrl4_c.drdy_mask; + + return ret; +} + +/** + * @brief HPF or SLOPE filter selection on wake-up and Activity/Inactivity + * functions.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slope_fds in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_hp_path_internal_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slope_fds_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.slope_fds = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief HPF or SLOPE filter selection on wake-up and Activity/Inactivity + * functions.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of slope_fds in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_hp_path_internal_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slope_fds_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + switch(tap_cfg.slope_fds) { + case LSM6DS3TR_C_USE_SLOPE: + *val = LSM6DS3TR_C_USE_SLOPE; + break; + + case LSM6DS3TR_C_USE_HPF: + *val = LSM6DS3TR_C_USE_HPF; + break; + + default: + *val = LSM6DS3TR_C_HP_PATH_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_accelerometer_filters + * @brief This section group all the functions concerning the filters + * configuration that impact accelerometer in every mode. + * @{ + * + */ + +/** + * @brief Accelerometer analog chain bandwidth selection (only for + * accelerometer ODR ≥ 1.67 kHz).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of bw0_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_filter_analog_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_bw0_xl_t val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ctrl1_xl.bw0_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + } + + return ret; +} + +/** + * @brief Accelerometer analog chain bandwidth selection (only for + * accelerometer ODR ≥ 1.67 kHz).[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of bw0_xl in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_filter_analog_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_bw0_xl_t* val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + switch(ctrl1_xl.bw0_xl) { + case LSM6DS3TR_C_XL_ANA_BW_1k5Hz: + *val = LSM6DS3TR_C_XL_ANA_BW_1k5Hz; + break; + + case LSM6DS3TR_C_XL_ANA_BW_400Hz: + *val = LSM6DS3TR_C_XL_ANA_BW_400Hz; + break; + + default: + *val = LSM6DS3TR_C_XL_ANA_BW_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_accelerometer_filters + * @brief This section group all the functions concerning the filters + * configuration that impact accelerometer. + * @{ + * + */ + +/** + * @brief Accelerometer digital LPF (LPF1) bandwidth selection LPF2 is + * not used.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of lpf1_bw_sel in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_lp1_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_bw_sel_t val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ctrl1_xl.lpf1_bw_sel = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.lpf2_xl_en = 0; + ctrl8_xl.hp_slope_xl_en = 0; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + } + } + + return ret; +} + +/** + * @brief Accelerometer digital LPF (LPF1) bandwidth selection LPF2 + * is not used.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of lpf1_bw_sel in reg CTRL1_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_lp1_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_bw_sel_t* val) { + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + if((ctrl8_xl.lpf2_xl_en != 0x00U) || (ctrl8_xl.hp_slope_xl_en != 0x00U)) { + *val = LSM6DS3TR_C_XL_LP1_NA; + } + + else { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL1_XL, (uint8_t*)&ctrl1_xl, 1); + + switch(ctrl1_xl.lpf1_bw_sel) { + case LSM6DS3TR_C_XL_LP1_ODR_DIV_2: + *val = LSM6DS3TR_C_XL_LP1_ODR_DIV_2; + break; + + case LSM6DS3TR_C_XL_LP1_ODR_DIV_4: + *val = LSM6DS3TR_C_XL_LP1_ODR_DIV_4; + break; + + default: + *val = LSM6DS3TR_C_XL_LP1_NA; + break; + } + } + } + + return ret; +} + +/** + * @brief LPF2 on outputs[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of input_composite in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_lp2_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_input_composite_t val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.input_composite = ((uint8_t)val & 0x10U) >> 4; + ctrl8_xl.hpcf_xl = (uint8_t)val & 0x03U; + ctrl8_xl.lpf2_xl_en = 1; + ctrl8_xl.hp_slope_xl_en = 0; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + + return ret; +} + +/** + * @brief LPF2 on outputs[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of input_composite in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_lp2_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_input_composite_t* val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + if((ctrl8_xl.lpf2_xl_en == 0x00U) || (ctrl8_xl.hp_slope_xl_en != 0x00U)) { + *val = LSM6DS3TR_C_XL_LP_NA; + } + + else { + switch((ctrl8_xl.input_composite << 4) + ctrl8_xl.hpcf_xl) { + case LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_50: + *val = LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_50; + break; + + case LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_100: + *val = LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_100; + break; + + case LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_9: + *val = LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_9; + break; + + case LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_400: + *val = LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_400; + break; + + case LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_50: + *val = LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_50; + break; + + case LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_100: + *val = LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_100; + break; + + case LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_9: + *val = LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_9; + break; + + case LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_400: + *val = LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_400; + break; + + default: + *val = LSM6DS3TR_C_XL_LP_NA; + break; + } + } + } + + return ret; +} + +/** + * @brief Enable HP filter reference mode.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of hp_ref_mode in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_reference_mode_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.hp_ref_mode = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + + return ret; +} + +/** + * @brief Enable HP filter reference mode.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of hp_ref_mode in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_reference_mode_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + *val = ctrl8_xl.hp_ref_mode; + + return ret; +} + +/** + * @brief High pass/Slope on outputs.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of hpcf_xl in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_hp_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_hpcf_xl_t val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.input_composite = 0; + ctrl8_xl.hpcf_xl = (uint8_t)val & 0x03U; + ctrl8_xl.hp_slope_xl_en = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + + return ret; +} + +/** + * @brief High pass/Slope on outputs.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of hpcf_xl in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_xl_hp_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_hpcf_xl_t* val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ctrl8_xl.hp_slope_xl_en == 0x00U) { + *val = LSM6DS3TR_C_XL_HP_NA; + } + + switch(ctrl8_xl.hpcf_xl) { + case LSM6DS3TR_C_XL_HP_ODR_DIV_4: + *val = LSM6DS3TR_C_XL_HP_ODR_DIV_4; + break; + + case LSM6DS3TR_C_XL_HP_ODR_DIV_100: + *val = LSM6DS3TR_C_XL_HP_ODR_DIV_100; + break; + + case LSM6DS3TR_C_XL_HP_ODR_DIV_9: + *val = LSM6DS3TR_C_XL_HP_ODR_DIV_9; + break; + + case LSM6DS3TR_C_XL_HP_ODR_DIV_400: + *val = LSM6DS3TR_C_XL_HP_ODR_DIV_400; + break; + + default: + *val = LSM6DS3TR_C_XL_HP_NA; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_gyroscope_filters + * @brief This section group all the functions concerning the filters + * configuration that impact gyroscope. + * @{ + * + */ + +/** + * @brief Gyroscope low pass path bandwidth.[set] + * + * @param ctx Read / write interface definitions + * @param val gyroscope filtering chain configuration. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_band_pass_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_sel_g_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + if(ret == 0) { + ctrl7_g.hpm_g = ((uint8_t)val & 0x30U) >> 4; + ctrl7_g.hp_en_g = ((uint8_t)val & 0x80U) >> 7; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ctrl6_c.ftype = (uint8_t)val & 0x03U; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.lpf1_sel_g = ((uint8_t)val & 0x08U) >> 3; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + } + } + } + } + + return ret; +} + +/** + * @brief Gyroscope low pass path bandwidth.[get] + * + * @param ctx Read / write interface definitions + * @param val gyroscope filtering chain + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_band_pass_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_sel_g_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL7_G, (uint8_t*)&ctrl7_g, 1); + + switch((ctrl7_g.hp_en_g << 7) + (ctrl7_g.hpm_g << 4) + (ctrl4_c.lpf1_sel_g << 3) + + ctrl6_c.ftype) { + case LSM6DS3TR_C_HP_16mHz_LP2: + *val = LSM6DS3TR_C_HP_16mHz_LP2; + break; + + case LSM6DS3TR_C_HP_65mHz_LP2: + *val = LSM6DS3TR_C_HP_65mHz_LP2; + break; + + case LSM6DS3TR_C_HP_260mHz_LP2: + *val = LSM6DS3TR_C_HP_260mHz_LP2; + break; + + case LSM6DS3TR_C_HP_1Hz04_LP2: + *val = LSM6DS3TR_C_HP_1Hz04_LP2; + break; + + case LSM6DS3TR_C_HP_DISABLE_LP1_LIGHT: + *val = LSM6DS3TR_C_HP_DISABLE_LP1_LIGHT; + break; + + case LSM6DS3TR_C_HP_DISABLE_LP1_NORMAL: + *val = LSM6DS3TR_C_HP_DISABLE_LP1_NORMAL; + break; + + case LSM6DS3TR_C_HP_DISABLE_LP_STRONG: + *val = LSM6DS3TR_C_HP_DISABLE_LP_STRONG; + break; + + case LSM6DS3TR_C_HP_DISABLE_LP1_AGGRESSIVE: + *val = LSM6DS3TR_C_HP_DISABLE_LP1_AGGRESSIVE; + break; + + case LSM6DS3TR_C_HP_16mHz_LP1_LIGHT: + *val = LSM6DS3TR_C_HP_16mHz_LP1_LIGHT; + break; + + case LSM6DS3TR_C_HP_65mHz_LP1_NORMAL: + *val = LSM6DS3TR_C_HP_65mHz_LP1_NORMAL; + break; + + case LSM6DS3TR_C_HP_260mHz_LP1_STRONG: + *val = LSM6DS3TR_C_HP_260mHz_LP1_STRONG; + break; + + case LSM6DS3TR_C_HP_1Hz04_LP1_AGGRESSIVE: + *val = LSM6DS3TR_C_HP_1Hz04_LP1_AGGRESSIVE; + break; + + default: + *val = LSM6DS3TR_C_HP_GY_BAND_NA; + break; + } + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_serial_interface + * @brief This section groups all the functions concerning serial + * interface management + * @{ + * + */ + +/** + * @brief SPI Serial Interface Mode selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sim in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_spi_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_sim_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.sim = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief SPI Serial Interface Mode selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of sim in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_spi_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_sim_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + switch(ctrl3_c.sim) { + case LSM6DS3TR_C_SPI_4_WIRE: + *val = LSM6DS3TR_C_SPI_4_WIRE; + break; + + case LSM6DS3TR_C_SPI_3_WIRE: + *val = LSM6DS3TR_C_SPI_3_WIRE; + break; + + default: + *val = LSM6DS3TR_C_SPI_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Disable / Enable I2C interface.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of i2c_disable in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_i2c_interface_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_i2c_disable_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.i2c_disable = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + return ret; +} + +/** + * @brief Disable / Enable I2C interface.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of i2c_disable in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_i2c_interface_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_i2c_disable_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + switch(ctrl4_c.i2c_disable) { + case LSM6DS3TR_C_I2C_ENABLE: + *val = LSM6DS3TR_C_I2C_ENABLE; + break; + + case LSM6DS3TR_C_I2C_DISABLE: + *val = LSM6DS3TR_C_I2C_DISABLE; + break; + + default: + *val = LSM6DS3TR_C_I2C_MODE_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_interrupt_pins + * @brief This section groups all the functions that manage + * interrupt pins + * @{ + * + */ + +/** + * @brief Select the signal that need to route on int1 pad[set] + * + * @param ctx Read / write interface definitions + * @param val configure INT1_CTRL, MD1_CFG, CTRL4_C(den_drdy_int1), + * MASTER_CONFIG(drdy_on_int1) + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_int1_route_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_int1_route_t val) { + lsm6ds3tr_c_master_config_t master_config; + lsm6ds3tr_c_int1_ctrl_t int1_ctrl; + lsm6ds3tr_c_md1_cfg_t md1_cfg; + lsm6ds3tr_c_md2_cfg_t md2_cfg; + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT1_CTRL, (uint8_t*)&int1_ctrl, 1); + + if(ret == 0) { + int1_ctrl.int1_drdy_xl = val.int1_drdy_xl; + int1_ctrl.int1_drdy_g = val.int1_drdy_g; + int1_ctrl.int1_boot = val.int1_boot; + int1_ctrl.int1_fth = val.int1_fth; + int1_ctrl.int1_fifo_ovr = val.int1_fifo_ovr; + int1_ctrl.int1_full_flag = val.int1_full_flag; + int1_ctrl.int1_sign_mot = val.int1_sign_mot; + int1_ctrl.int1_step_detector = val.int1_step_detector; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT1_CTRL, (uint8_t*)&int1_ctrl, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD1_CFG, (uint8_t*)&md1_cfg, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD2_CFG, (uint8_t*)&md2_cfg, 1); + } + + if(ret == 0) { + md1_cfg.int1_timer = val.int1_timer; + md1_cfg.int1_tilt = val.int1_tilt; + md1_cfg.int1_6d = val.int1_6d; + md1_cfg.int1_double_tap = val.int1_double_tap; + md1_cfg.int1_ff = val.int1_ff; + md1_cfg.int1_wu = val.int1_wu; + md1_cfg.int1_single_tap = val.int1_single_tap; + md1_cfg.int1_inact_state = val.int1_inact_state; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MD1_CFG, (uint8_t*)&md1_cfg, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + if(ret == 0) { + ctrl4_c.den_drdy_int1 = val.den_drdy_int1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + if(ret == 0) { + master_config.drdy_on_int1 = val.den_drdy_int1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if((val.int1_6d != 0x00U) || (val.int1_ff != 0x00U) || (val.int1_wu != 0x00U) || + (val.int1_single_tap != 0x00U) || (val.int1_double_tap != 0x00U) || + (val.int1_inact_state != 0x00U) || (md2_cfg.int2_6d != 0x00U) || + (md2_cfg.int2_ff != 0x00U) || (md2_cfg.int2_wu != 0x00U) || + (md2_cfg.int2_single_tap != 0x00U) || (md2_cfg.int2_double_tap != 0x00U) || + (md2_cfg.int2_inact_state != 0x00U)) { + tap_cfg.interrupts_enable = PROPERTY_ENABLE; + } + + else { + tap_cfg.interrupts_enable = PROPERTY_DISABLE; + } + } + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Select the signal that need to route on int1 pad[get] + * + * @param ctx Read / write interface definitions + * @param val read INT1_CTRL, MD1_CFG, CTRL4_C(den_drdy_int1), + * MASTER_CONFIG(drdy_on_int1) + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_int1_route_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_int1_route_t* val) { + lsm6ds3tr_c_master_config_t master_config; + lsm6ds3tr_c_int1_ctrl_t int1_ctrl; + lsm6ds3tr_c_md1_cfg_t md1_cfg; + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT1_CTRL, (uint8_t*)&int1_ctrl, 1); + + if(ret == 0) { + val->int1_drdy_xl = int1_ctrl.int1_drdy_xl; + val->int1_drdy_g = int1_ctrl.int1_drdy_g; + val->int1_boot = int1_ctrl.int1_boot; + val->int1_fth = int1_ctrl.int1_fth; + val->int1_fifo_ovr = int1_ctrl.int1_fifo_ovr; + val->int1_full_flag = int1_ctrl.int1_full_flag; + val->int1_sign_mot = int1_ctrl.int1_sign_mot; + val->int1_step_detector = int1_ctrl.int1_step_detector; + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD1_CFG, (uint8_t*)&md1_cfg, 1); + + if(ret == 0) { + val->int1_timer = md1_cfg.int1_timer; + val->int1_tilt = md1_cfg.int1_tilt; + val->int1_6d = md1_cfg.int1_6d; + val->int1_double_tap = md1_cfg.int1_double_tap; + val->int1_ff = md1_cfg.int1_ff; + val->int1_wu = md1_cfg.int1_wu; + val->int1_single_tap = md1_cfg.int1_single_tap; + val->int1_inact_state = md1_cfg.int1_inact_state; + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + val->den_drdy_int1 = ctrl4_c.den_drdy_int1; + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + val->den_drdy_int1 = master_config.drdy_on_int1; + } + } + } + + return ret; +} + +/** + * @brief Select the signal that need to route on int2 pad[set] + * + * @param ctx Read / write interface definitions + * @param val INT2_CTRL, DRDY_PULSE_CFG(int2_wrist_tilt), MD2_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_int2_route_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_int2_route_t val) { + lsm6ds3tr_c_int2_ctrl_t int2_ctrl; + lsm6ds3tr_c_md1_cfg_t md1_cfg; + lsm6ds3tr_c_md2_cfg_t md2_cfg; + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT2_CTRL, (uint8_t*)&int2_ctrl, 1); + + if(ret == 0) { + int2_ctrl.int2_drdy_xl = val.int2_drdy_xl; + int2_ctrl.int2_drdy_g = val.int2_drdy_g; + int2_ctrl.int2_drdy_temp = val.int2_drdy_temp; + int2_ctrl.int2_fth = val.int2_fth; + int2_ctrl.int2_fifo_ovr = val.int2_fifo_ovr; + int2_ctrl.int2_full_flag = val.int2_full_flag; + int2_ctrl.int2_step_count_ov = val.int2_step_count_ov; + int2_ctrl.int2_step_delta = val.int2_step_delta; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT2_CTRL, (uint8_t*)&int2_ctrl, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD1_CFG, (uint8_t*)&md1_cfg, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD2_CFG, (uint8_t*)&md2_cfg, 1); + } + + if(ret == 0) { + md2_cfg.int2_iron = val.int2_iron; + md2_cfg.int2_tilt = val.int2_tilt; + md2_cfg.int2_6d = val.int2_6d; + md2_cfg.int2_double_tap = val.int2_double_tap; + md2_cfg.int2_ff = val.int2_ff; + md2_cfg.int2_wu = val.int2_wu; + md2_cfg.int2_single_tap = val.int2_single_tap; + md2_cfg.int2_inact_state = val.int2_inact_state; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MD2_CFG, (uint8_t*)&md2_cfg, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + } + + if(ret == 0) { + drdy_pulse_cfg_g.int2_wrist_tilt = val.int2_wrist_tilt; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + } + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if((md1_cfg.int1_6d != 0x00U) || (md1_cfg.int1_ff != 0x00U) || + (md1_cfg.int1_wu != 0x00U) || (md1_cfg.int1_single_tap != 0x00U) || + (md1_cfg.int1_double_tap != 0x00U) || (md1_cfg.int1_inact_state != 0x00U) || + (val.int2_6d != 0x00U) || (val.int2_ff != 0x00U) || (val.int2_wu != 0x00U) || + (val.int2_single_tap != 0x00U) || (val.int2_double_tap != 0x00U) || + (val.int2_inact_state != 0x00U)) { + tap_cfg.interrupts_enable = PROPERTY_ENABLE; + } + + else { + tap_cfg.interrupts_enable = PROPERTY_DISABLE; + } + } + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Select the signal that need to route on int2 pad[get] + * + * @param ctx Read / write interface definitions + * @param val INT2_CTRL, DRDY_PULSE_CFG(int2_wrist_tilt), MD2_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_int2_route_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_int2_route_t* val) { + lsm6ds3tr_c_int2_ctrl_t int2_ctrl; + lsm6ds3tr_c_md2_cfg_t md2_cfg; + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT2_CTRL, (uint8_t*)&int2_ctrl, 1); + + if(ret == 0) { + val->int2_drdy_xl = int2_ctrl.int2_drdy_xl; + val->int2_drdy_g = int2_ctrl.int2_drdy_g; + val->int2_drdy_temp = int2_ctrl.int2_drdy_temp; + val->int2_fth = int2_ctrl.int2_fth; + val->int2_fifo_ovr = int2_ctrl.int2_fifo_ovr; + val->int2_full_flag = int2_ctrl.int2_full_flag; + val->int2_step_count_ov = int2_ctrl.int2_step_count_ov; + val->int2_step_delta = int2_ctrl.int2_step_delta; + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MD2_CFG, (uint8_t*)&md2_cfg, 1); + + if(ret == 0) { + val->int2_iron = md2_cfg.int2_iron; + val->int2_tilt = md2_cfg.int2_tilt; + val->int2_6d = md2_cfg.int2_6d; + val->int2_double_tap = md2_cfg.int2_double_tap; + val->int2_ff = md2_cfg.int2_ff; + val->int2_wu = md2_cfg.int2_wu; + val->int2_single_tap = md2_cfg.int2_single_tap; + val->int2_inact_state = md2_cfg.int2_inact_state; + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_DRDY_PULSE_CFG_G, (uint8_t*)&drdy_pulse_cfg_g, 1); + val->int2_wrist_tilt = drdy_pulse_cfg_g.int2_wrist_tilt; + } + } + + return ret; +} + +/** + * @brief Push-pull/open drain selection on interrupt pads.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pp_od in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pp_od_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.pp_od = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Push-pull/open drain selection on interrupt pads.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of pp_od in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pp_od_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + switch(ctrl3_c.pp_od) { + case LSM6DS3TR_C_PUSH_PULL: + *val = LSM6DS3TR_C_PUSH_PULL; + break; + + case LSM6DS3TR_C_OPEN_DRAIN: + *val = LSM6DS3TR_C_OPEN_DRAIN; + break; + + default: + *val = LSM6DS3TR_C_PIN_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Interrupt active-high/low.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of h_lactive in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_polarity_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_h_lactive_t val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + if(ret == 0) { + ctrl3_c.h_lactive = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + } + + return ret; +} + +/** + * @brief Interrupt active-high/low.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of h_lactive in reg CTRL3_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pin_polarity_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_h_lactive_t* val) { + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL3_C, (uint8_t*)&ctrl3_c, 1); + + switch(ctrl3_c.h_lactive) { + case LSM6DS3TR_C_ACTIVE_HIGH: + *val = LSM6DS3TR_C_ACTIVE_HIGH; + break; + + case LSM6DS3TR_C_ACTIVE_LOW: + *val = LSM6DS3TR_C_ACTIVE_LOW; + break; + + default: + *val = LSM6DS3TR_C_POLARITY_ND; + break; + } + + return ret; +} + +/** + * @brief All interrupt signals become available on INT1 pin.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of int2_on_int1 in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_all_on_int1_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.int2_on_int1 = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + return ret; +} + +/** + * @brief All interrupt signals become available on INT1 pin.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of int2_on_int1 in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_all_on_int1_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + *val = ctrl4_c.int2_on_int1; + + return ret; +} + +/** + * @brief Latched/pulsed interrupt.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of lir in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_int_notification_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lir_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.lir = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Latched/pulsed interrupt.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of lir in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_int_notification_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lir_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + switch(tap_cfg.lir) { + case LSM6DS3TR_C_INT_PULSED: + *val = LSM6DS3TR_C_INT_PULSED; + break; + + case LSM6DS3TR_C_INT_LATCHED: + *val = LSM6DS3TR_C_INT_LATCHED; + break; + + default: + *val = LSM6DS3TR_C_INT_MODE; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Wake_Up_event + * @brief This section groups all the functions that manage the + * Wake Up event generation. + * @{ + * + */ + +/** + * @brief Threshold for wakeup.1 LSB = FS_XL / 64.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of wk_ths in reg WAKE_UP_THS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wkup_threshold_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + + if(ret == 0) { + wake_up_ths.wk_ths = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + } + + return ret; +} + +/** + * @brief Threshold for wakeup.1 LSB = FS_XL / 64.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of wk_ths in reg WAKE_UP_THS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wkup_threshold_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + *val = wake_up_ths.wk_ths; + + return ret; +} + +/** + * @brief Wake up duration event.1LSb = 1 / ODR[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of wake_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wkup_dur_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + wake_up_dur.wake_dur = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + } + + return ret; +} + +/** + * @brief Wake up duration event.1LSb = 1 / ODR[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of wake_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wkup_dur_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + *val = wake_up_dur.wake_dur; + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Activity/Inactivity_detection + * @brief This section groups all the functions concerning + * activity/inactivity detection. + * @{ + * + */ + +/** + * @brief Enables gyroscope Sleep mode.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sleep in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_sleep_mode_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.sleep = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + + return ret; +} + +/** + * @brief Enables gyroscope Sleep mode.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sleep in reg CTRL4_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_gy_sleep_mode_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + *val = ctrl4_c.sleep; + + return ret; +} + +/** + * @brief Enable inactivity function.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of inact_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_act_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_inact_en_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.inact_en = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Enable inactivity function.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of inact_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_act_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_inact_en_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + switch(tap_cfg.inact_en) { + case LSM6DS3TR_C_PROPERTY_DISABLE: + *val = LSM6DS3TR_C_PROPERTY_DISABLE; + break; + + case LSM6DS3TR_C_XL_12Hz5_GY_NOT_AFFECTED: + *val = LSM6DS3TR_C_XL_12Hz5_GY_NOT_AFFECTED; + break; + + case LSM6DS3TR_C_XL_12Hz5_GY_SLEEP: + *val = LSM6DS3TR_C_XL_12Hz5_GY_SLEEP; + break; + + case LSM6DS3TR_C_XL_12Hz5_GY_PD: + *val = LSM6DS3TR_C_XL_12Hz5_GY_PD; + break; + + default: + *val = LSM6DS3TR_C_ACT_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Duration to go in sleep mode.1 LSb = 512 / ODR[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sleep_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_act_sleep_dur_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + wake_up_dur.sleep_dur = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + } + + return ret; +} + +/** + * @brief Duration to go in sleep mode. 1 LSb = 512 / ODR[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sleep_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_act_sleep_dur_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + *val = wake_up_dur.sleep_dur; + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_tap_generator + * @brief This section groups all the functions that manage the + * tap and double tap event generation. + * @{ + * + */ + +/** + * @brief Read the tap / double tap source register.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure of registers from TAP_SRC + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_src_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_tap_src_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_SRC, (uint8_t*)val, 1); + + return ret; +} + +/** + * @brief Enable Z direction in tap recognition.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_z_en in reg TAP_CFG + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_z_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.tap_z_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Enable Z direction in tap recognition.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_z_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_z_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + *val = tap_cfg.tap_z_en; + + return ret; +} + +/** + * @brief Enable Y direction in tap recognition.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_y_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_y_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.tap_y_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Enable Y direction in tap recognition.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_y_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_y_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + *val = tap_cfg.tap_y_en; + + return ret; +} + +/** + * @brief Enable X direction in tap recognition.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_x_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_x_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + + if(ret == 0) { + tap_cfg.tap_x_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + } + + return ret; +} + +/** + * @brief Enable X direction in tap recognition.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_x_en in reg TAP_CFG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_detection_on_x_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_cfg_t tap_cfg; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_CFG, (uint8_t*)&tap_cfg, 1); + *val = tap_cfg.tap_x_en; + + return ret; +} + +/** + * @brief Threshold for tap recognition.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_ths in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_threshold_x_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + + if(ret == 0) { + tap_ths_6d.tap_ths = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + } + + return ret; +} + +/** + * @brief Threshold for tap recognition.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tap_ths in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_threshold_x_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + *val = tap_ths_6d.tap_ths; + + return ret; +} + +/** + * @brief Maximum duration is the maximum time of an overthreshold signal + * detection to be recognized as a tap event. + * The default value of these bits is 00b which corresponds to + * 4*ODR_XL time. + * If the SHOCK[1:0] bits are set to a different + * value, 1LSB corresponds to 8*ODR_XL time.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of shock in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_shock_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + + if(ret == 0) { + int_dur2.shock = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + } + + return ret; +} + +/** + * @brief Maximum duration is the maximum time of an overthreshold signal + * detection to be recognized as a tap event. + * The default value of these bits is 00b which corresponds to + * 4*ODR_XL time. + * If the SHOCK[1:0] bits are set to a different value, 1LSB + * corresponds to 8*ODR_XL time.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of shock in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_shock_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + *val = int_dur2.shock; + + return ret; +} + +/** + * @brief Quiet time is the time after the first detected tap in which there + * must not be any overthreshold event. + * The default value of these bits is 00b which corresponds to + * 2*ODR_XL time. + * If the QUIET[1:0] bits are set to a different value, 1LSB + * corresponds to 4*ODR_XL time.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of quiet in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_quiet_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + + if(ret == 0) { + int_dur2.quiet = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + } + + return ret; +} + +/** + * @brief Quiet time is the time after the first detected tap in which there + * must not be any overthreshold event. + * The default value of these bits is 00b which corresponds to + * 2*ODR_XL time. + * If the QUIET[1:0] bits are set to a different value, 1LSB + * corresponds to 4*ODR_XL time.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of quiet in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_quiet_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + *val = int_dur2.quiet; + + return ret; +} + +/** + * @brief When double tap recognition is enabled, this register expresses the + * maximum time between two consecutive detected taps to determine a + * double tap event. + * The default value of these bits is 0000b which corresponds to + * 16*ODR_XL time. + * If the DUR[3:0] bits are set to a different value,1LSB corresponds + * to 32*ODR_XL time.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dur in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_dur_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + + if(ret == 0) { + int_dur2.dur = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + } + + return ret; +} + +/** + * @brief When double tap recognition is enabled, this register expresses the + * maximum time between two consecutive detected taps to determine a + * double tap event. + * The default value of these bits is 0000b which corresponds to + * 16*ODR_XL time. + * If the DUR[3:0] bits are set to a different value,1LSB corresponds + * to 32*ODR_XL time.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dur in reg INT_DUR2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_dur_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_int_dur2_t int_dur2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_INT_DUR2, (uint8_t*)&int_dur2, 1); + *val = int_dur2.dur; + + return ret; +} + +/** + * @brief Single/double-tap event enable/disable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of + * single_double_tap in reg WAKE_UP_THS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_single_double_tap_t val) { + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + + if(ret == 0) { + wake_up_ths.single_double_tap = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + } + + return ret; +} + +/** + * @brief Single/double-tap event enable/disable.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of single_double_tap + * in reg WAKE_UP_THS + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tap_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_single_double_tap_t* val) { + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_THS, (uint8_t*)&wake_up_ths, 1); + + switch(wake_up_ths.single_double_tap) { + case LSM6DS3TR_C_ONLY_SINGLE: + *val = LSM6DS3TR_C_ONLY_SINGLE; + break; + + case LSM6DS3TR_C_BOTH_SINGLE_DOUBLE: + *val = LSM6DS3TR_C_BOTH_SINGLE_DOUBLE; + break; + + default: + *val = LSM6DS3TR_C_TAP_MODE_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_ Six_position_detection(6D/4D) + * @brief This section groups all the functions concerning six + * position detection (6D). + * @{ + * + */ + +/** + * @brief LPF2 feed 6D function selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of low_pass_on_6d in + * reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_6d_feed_data_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_low_pass_on_6d_t val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + if(ret == 0) { + ctrl8_xl.low_pass_on_6d = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + } + + return ret; +} + +/** + * @brief LPF2 feed 6D function selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of low_pass_on_6d in reg CTRL8_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_6d_feed_data_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_low_pass_on_6d_t* val) { + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL8_XL, (uint8_t*)&ctrl8_xl, 1); + + switch(ctrl8_xl.low_pass_on_6d) { + case LSM6DS3TR_C_ODR_DIV_2_FEED: + *val = LSM6DS3TR_C_ODR_DIV_2_FEED; + break; + + case LSM6DS3TR_C_LPF2_FEED: + *val = LSM6DS3TR_C_LPF2_FEED; + break; + + default: + *val = LSM6DS3TR_C_6D_FEED_ND; + break; + } + + return ret; +} + +/** + * @brief Threshold for 4D/6D function.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sixd_ths in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_6d_threshold_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_sixd_ths_t val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + + if(ret == 0) { + tap_ths_6d.sixd_ths = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + } + + return ret; +} + +/** + * @brief Threshold for 4D/6D function.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of sixd_ths in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_6d_threshold_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_sixd_ths_t* val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + + switch(tap_ths_6d.sixd_ths) { + case LSM6DS3TR_C_DEG_80: + *val = LSM6DS3TR_C_DEG_80; + break; + + case LSM6DS3TR_C_DEG_70: + *val = LSM6DS3TR_C_DEG_70; + break; + + case LSM6DS3TR_C_DEG_60: + *val = LSM6DS3TR_C_DEG_60; + break; + + case LSM6DS3TR_C_DEG_50: + *val = LSM6DS3TR_C_DEG_50; + break; + + default: + *val = LSM6DS3TR_C_6D_TH_ND; + break; + } + + return ret; +} + +/** + * @brief 4D orientation detection enable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of d4d_en in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_4d_mode_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + + if(ret == 0) { + tap_ths_6d.d4d_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + } + + return ret; +} + +/** + * @brief 4D orientation detection enable.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of d4d_en in reg TAP_THS_6D + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_4d_mode_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_TAP_THS_6D, (uint8_t*)&tap_ths_6d, 1); + *val = tap_ths_6d.d4d_en; + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_free_fall + * @brief This section group all the functions concerning the free + * fall detection. + * @{ + * + */ + +/** + * @brief Free-fall duration event. 1LSb = 1 / ODR[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ff_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_ff_dur_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + lsm6ds3tr_c_free_fall_t free_fall; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + + if(ret == 0) { + free_fall.ff_dur = (val & 0x1FU); + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + wake_up_dur.ff_dur = (val & 0x20U) >> 5; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + } + } + } + + return ret; +} + +/** + * @brief Free-fall duration event. 1LSb = 1 / ODR[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ff_dur in reg WAKE_UP_DUR + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_ff_dur_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + lsm6ds3tr_c_free_fall_t free_fall; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_WAKE_UP_DUR, (uint8_t*)&wake_up_dur, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + } + + *val = (wake_up_dur.ff_dur << 5) + free_fall.ff_dur; + + return ret; +} + +/** + * @brief Free fall threshold setting.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ff_ths in reg FREE_FALL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_ff_threshold_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_ff_ths_t val) { + lsm6ds3tr_c_free_fall_t free_fall; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + + if(ret == 0) { + free_fall.ff_ths = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + } + + return ret; +} + +/** + * @brief Free fall threshold setting.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of ff_ths in reg FREE_FALL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_ff_threshold_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_ff_ths_t* val) { + lsm6ds3tr_c_free_fall_t free_fall; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FREE_FALL, (uint8_t*)&free_fall, 1); + + switch(free_fall.ff_ths) { + case LSM6DS3TR_C_FF_TSH_156mg: + *val = LSM6DS3TR_C_FF_TSH_156mg; + break; + + case LSM6DS3TR_C_FF_TSH_219mg: + *val = LSM6DS3TR_C_FF_TSH_219mg; + break; + + case LSM6DS3TR_C_FF_TSH_250mg: + *val = LSM6DS3TR_C_FF_TSH_250mg; + break; + + case LSM6DS3TR_C_FF_TSH_312mg: + *val = LSM6DS3TR_C_FF_TSH_312mg; + break; + + case LSM6DS3TR_C_FF_TSH_344mg: + *val = LSM6DS3TR_C_FF_TSH_344mg; + break; + + case LSM6DS3TR_C_FF_TSH_406mg: + *val = LSM6DS3TR_C_FF_TSH_406mg; + break; + + case LSM6DS3TR_C_FF_TSH_469mg: + *val = LSM6DS3TR_C_FF_TSH_469mg; + break; + + case LSM6DS3TR_C_FF_TSH_500mg: + *val = LSM6DS3TR_C_FF_TSH_500mg; + break; + + default: + *val = LSM6DS3TR_C_FF_TSH_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_fifo + * @brief This section group all the functions concerning the + * fifo usage + * @{ + * + */ + +/** + * @brief FIFO watermark level selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fth in reg FIFO_CTRL1 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_watermark_set(stmdev_ctx_t* ctx, uint16_t val) { + lsm6ds3tr_c_fifo_ctrl1_t fifo_ctrl1; + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + fifo_ctrl1.fth = (uint8_t)(0x00FFU & val); + fifo_ctrl2.fth = (uint8_t)((0x0700U & val) >> 8); + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL1, (uint8_t*)&fifo_ctrl1, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + } + } + + return ret; +} + +/** + * @brief FIFO watermark level selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fth in reg FIFO_CTRL1 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_watermark_get(stmdev_ctx_t* ctx, uint16_t* val) { + lsm6ds3tr_c_fifo_ctrl1_t fifo_ctrl1; + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL1, (uint8_t*)&fifo_ctrl1, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + } + + *val = ((uint16_t)fifo_ctrl2.fth << 8) + (uint16_t)fifo_ctrl1.fth; + + return ret; +} + +/** + * @brief FIFO data level.[get] + * + * @param ctx Read / write interface definitions + * @param val get the values of diff_fifo in reg FIFO_STATUS1 and + * FIFO_STATUS2(diff_fifo), it is recommended to set the + * BDU bit. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_data_level_get(stmdev_ctx_t* ctx, uint16_t* val) { + lsm6ds3tr_c_fifo_status1_t fifo_status1; + lsm6ds3tr_c_fifo_status2_t fifo_status2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS1, (uint8_t*)&fifo_status1, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS2, (uint8_t*)&fifo_status2, 1); + *val = ((uint16_t)fifo_status2.diff_fifo << 8) + (uint16_t)fifo_status1.diff_fifo; + } + + return ret; +} + +/** + * @brief FIFO watermark.[get] + * + * @param ctx Read / write interface definitions + * @param val get the values of watermark in reg FIFO_STATUS2 and + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_wtm_flag_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_status2_t fifo_status2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS2, (uint8_t*)&fifo_status2, 1); + *val = fifo_status2.waterm; + + return ret; +} + +/** + * @brief FIFO pattern.[get] + * + * @param ctx Read / write interface definitions + * @param val get the values of fifo_pattern in reg FIFO_STATUS3 and + * FIFO_STATUS4, it is recommended to set the BDU bit + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_pattern_get(stmdev_ctx_t* ctx, uint16_t* val) { + lsm6ds3tr_c_fifo_status3_t fifo_status3; + lsm6ds3tr_c_fifo_status4_t fifo_status4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS3, (uint8_t*)&fifo_status3, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_STATUS4, (uint8_t*)&fifo_status4, 1); + *val = ((uint16_t)fifo_status4.fifo_pattern << 8) + fifo_status3.fifo_pattern; + } + + return ret; +} + +/** + * @brief Batching of temperature data[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fifo_temp_en in reg FIFO_CTRL2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_temp_batch_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + fifo_ctrl2.fifo_temp_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + } + + return ret; +} + +/** + * @brief Batching of temperature data[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fifo_temp_en in reg FIFO_CTRL2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_temp_batch_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + *val = fifo_ctrl2.fifo_temp_en; + + return ret; +} + +/** + * @brief Trigger signal for FIFO write operation.[set] + * + * @param ctx Read / write interface definitions + * @param val act on FIFO_CTRL2(timer_pedo_fifo_drdy) + * and MASTER_CONFIG(data_valid_sel_fifo) + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_write_trigger_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_trigger_fifo_t val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + fifo_ctrl2.timer_pedo_fifo_drdy = (uint8_t)val & 0x01U; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + ret = + lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.data_valid_sel_fifo = (((uint8_t)val & 0x02U) >> 1); + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + } + } + + return ret; +} + +/** + * @brief Trigger signal for FIFO write operation.[get] + * + * @param ctx Read / write interface definitions + * @param val act on FIFO_CTRL2(timer_pedo_fifo_drdy) + * and MASTER_CONFIG(data_valid_sel_fifo) + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_write_trigger_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_trigger_fifo_t* val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + switch((fifo_ctrl2.timer_pedo_fifo_drdy << 1) + fifo_ctrl2.timer_pedo_fifo_drdy) { + case LSM6DS3TR_C_TRG_XL_GY_DRDY: + *val = LSM6DS3TR_C_TRG_XL_GY_DRDY; + break; + + case LSM6DS3TR_C_TRG_STEP_DETECT: + *val = LSM6DS3TR_C_TRG_STEP_DETECT; + break; + + case LSM6DS3TR_C_TRG_SH_DRDY: + *val = LSM6DS3TR_C_TRG_SH_DRDY; + break; + + default: + *val = LSM6DS3TR_C_TRG_SH_ND; + break; + } + } + + return ret; +} + +/** + * @brief Enable pedometer step counter and timestamp as 4th + * FIFO data set.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_pedo_fifo_en in reg FIFO_CTRL2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_pedo_and_timestamp_batch_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + + if(ret == 0) { + fifo_ctrl2.timer_pedo_fifo_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + } + + return ret; +} + +/** + * @brief Enable pedometer step counter and timestamp as 4th + * FIFO data set.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of timer_pedo_fifo_en in reg FIFO_CTRL2 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_pedo_and_timestamp_batch_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL2, (uint8_t*)&fifo_ctrl2, 1); + *val = fifo_ctrl2.timer_pedo_fifo_en; + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) for + * accelerometer data.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dec_fifo_xl in reg FIFO_CTRL3 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_xl_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_xl_t val) { + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + + if(ret == 0) { + fifo_ctrl3.dec_fifo_xl = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) for + * accelerometer data.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of dec_fifo_xl in reg FIFO_CTRL3 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_xl_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_xl_t* val) { + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + + switch(fifo_ctrl3.dec_fifo_xl) { + case LSM6DS3TR_C_FIFO_XL_DISABLE: + *val = LSM6DS3TR_C_FIFO_XL_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_XL_NO_DEC: + *val = LSM6DS3TR_C_FIFO_XL_NO_DEC; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_2: + *val = LSM6DS3TR_C_FIFO_XL_DEC_2; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_3: + *val = LSM6DS3TR_C_FIFO_XL_DEC_3; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_4: + *val = LSM6DS3TR_C_FIFO_XL_DEC_4; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_8: + *val = LSM6DS3TR_C_FIFO_XL_DEC_8; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_16: + *val = LSM6DS3TR_C_FIFO_XL_DEC_16; + break; + + case LSM6DS3TR_C_FIFO_XL_DEC_32: + *val = LSM6DS3TR_C_FIFO_XL_DEC_32; + break; + + default: + *val = LSM6DS3TR_C_FIFO_XL_DEC_ND; + break; + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for gyroscope data.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dec_fifo_gyro in reg FIFO_CTRL3 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_gy_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_gyro_t val) { + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + + if(ret == 0) { + fifo_ctrl3.dec_fifo_gyro = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for gyroscope data.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of dec_fifo_gyro in reg FIFO_CTRL3 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_gy_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_gyro_t* val) { + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL3, (uint8_t*)&fifo_ctrl3, 1); + + switch(fifo_ctrl3.dec_fifo_gyro) { + case LSM6DS3TR_C_FIFO_GY_DISABLE: + *val = LSM6DS3TR_C_FIFO_GY_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_GY_NO_DEC: + *val = LSM6DS3TR_C_FIFO_GY_NO_DEC; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_2: + *val = LSM6DS3TR_C_FIFO_GY_DEC_2; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_3: + *val = LSM6DS3TR_C_FIFO_GY_DEC_3; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_4: + *val = LSM6DS3TR_C_FIFO_GY_DEC_4; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_8: + *val = LSM6DS3TR_C_FIFO_GY_DEC_8; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_16: + *val = LSM6DS3TR_C_FIFO_GY_DEC_16; + break; + + case LSM6DS3TR_C_FIFO_GY_DEC_32: + *val = LSM6DS3TR_C_FIFO_GY_DEC_32; + break; + + default: + *val = LSM6DS3TR_C_FIFO_GY_DEC_ND; + break; + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for third data set.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dec_ds3_fifo in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_dataset_3_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds3_fifo_t val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + if(ret == 0) { + fifo_ctrl4.dec_ds3_fifo = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for third data set.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of dec_ds3_fifo in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_dataset_3_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds3_fifo_t* val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + switch(fifo_ctrl4.dec_ds3_fifo) { + case LSM6DS3TR_C_FIFO_DS3_DISABLE: + *val = LSM6DS3TR_C_FIFO_DS3_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_DS3_NO_DEC: + *val = LSM6DS3TR_C_FIFO_DS3_NO_DEC; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_2: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_2; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_3: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_3; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_4: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_4; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_8: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_8; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_16: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_16; + break; + + case LSM6DS3TR_C_FIFO_DS3_DEC_32: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_32; + break; + + default: + *val = LSM6DS3TR_C_FIFO_DS3_DEC_ND; + break; + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) + * for fourth data set.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of dec_ds4_fifo in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_dataset_4_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds4_fifo_t val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + if(ret == 0) { + fifo_ctrl4.dec_ds4_fifo = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + } + + return ret; +} + +/** + * @brief Selects Batching Data Rate (writing frequency in FIFO) for + * fourth data set.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of dec_ds4_fifo in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_dataset_4_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds4_fifo_t* val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + switch(fifo_ctrl4.dec_ds4_fifo) { + case LSM6DS3TR_C_FIFO_DS4_DISABLE: + *val = LSM6DS3TR_C_FIFO_DS4_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_DS4_NO_DEC: + *val = LSM6DS3TR_C_FIFO_DS4_NO_DEC; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_2: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_2; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_3: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_3; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_4: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_4; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_8: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_8; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_16: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_16; + break; + + case LSM6DS3TR_C_FIFO_DS4_DEC_32: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_32; + break; + + default: + *val = LSM6DS3TR_C_FIFO_DS4_DEC_ND; + break; + } + + return ret; +} + +/** + * @brief 8-bit data storage in FIFO.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of only_high_data in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_xl_gy_8bit_format_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + if(ret == 0) { + fifo_ctrl4.only_high_data = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + } + + return ret; +} + +/** + * @brief 8-bit data storage in FIFO.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of only_high_data in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_xl_gy_8bit_format_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + *val = fifo_ctrl4.only_high_data; + + return ret; +} + +/** + * @brief Sensing chain FIFO stop values memorization at threshold + * level.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of stop_on_fth in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_stop_on_wtm_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + + if(ret == 0) { + fifo_ctrl4.stop_on_fth = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + } + + return ret; +} + +/** + * @brief Sensing chain FIFO stop values memorization at threshold + * level.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of stop_on_fth in reg FIFO_CTRL4 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_stop_on_wtm_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL4, (uint8_t*)&fifo_ctrl4, 1); + *val = fifo_ctrl4.stop_on_fth; + + return ret; +} + +/** + * @brief FIFO mode selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of fifo_mode in reg FIFO_CTRL5 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fifo_mode_t val) { + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + + if(ret == 0) { + fifo_ctrl5.fifo_mode = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + } + + return ret; +} + +/** + * @brief FIFO mode selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of fifo_mode in reg FIFO_CTRL5 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fifo_mode_t* val) { + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + + switch(fifo_ctrl5.fifo_mode) { + case LSM6DS3TR_C_BYPASS_MODE: + *val = LSM6DS3TR_C_BYPASS_MODE; + break; + + case LSM6DS3TR_C_FIFO_MODE: + *val = LSM6DS3TR_C_FIFO_MODE; + break; + + case LSM6DS3TR_C_STREAM_TO_FIFO_MODE: + *val = LSM6DS3TR_C_STREAM_TO_FIFO_MODE; + break; + + case LSM6DS3TR_C_BYPASS_TO_STREAM_MODE: + *val = LSM6DS3TR_C_BYPASS_TO_STREAM_MODE; + break; + + case LSM6DS3TR_C_STREAM_MODE: + *val = LSM6DS3TR_C_STREAM_MODE; + break; + + default: + *val = LSM6DS3TR_C_FIFO_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief FIFO ODR selection, setting FIFO_MODE also.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of odr_fifo in reg FIFO_CTRL5 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_fifo_t val) { + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + + if(ret == 0) { + fifo_ctrl5.odr_fifo = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + } + + return ret; +} + +/** + * @brief FIFO ODR selection, setting FIFO_MODE also.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of odr_fifo in reg FIFO_CTRL5 + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_fifo_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_fifo_t* val) { + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_FIFO_CTRL5, (uint8_t*)&fifo_ctrl5, 1); + + switch(fifo_ctrl5.odr_fifo) { + case LSM6DS3TR_C_FIFO_DISABLE: + *val = LSM6DS3TR_C_FIFO_DISABLE; + break; + + case LSM6DS3TR_C_FIFO_12Hz5: + *val = LSM6DS3TR_C_FIFO_12Hz5; + break; + + case LSM6DS3TR_C_FIFO_26Hz: + *val = LSM6DS3TR_C_FIFO_26Hz; + break; + + case LSM6DS3TR_C_FIFO_52Hz: + *val = LSM6DS3TR_C_FIFO_52Hz; + break; + + case LSM6DS3TR_C_FIFO_104Hz: + *val = LSM6DS3TR_C_FIFO_104Hz; + break; + + case LSM6DS3TR_C_FIFO_208Hz: + *val = LSM6DS3TR_C_FIFO_208Hz; + break; + + case LSM6DS3TR_C_FIFO_416Hz: + *val = LSM6DS3TR_C_FIFO_416Hz; + break; + + case LSM6DS3TR_C_FIFO_833Hz: + *val = LSM6DS3TR_C_FIFO_833Hz; + break; + + case LSM6DS3TR_C_FIFO_1k66Hz: + *val = LSM6DS3TR_C_FIFO_1k66Hz; + break; + + case LSM6DS3TR_C_FIFO_3k33Hz: + *val = LSM6DS3TR_C_FIFO_3k33Hz; + break; + + case LSM6DS3TR_C_FIFO_6k66Hz: + *val = LSM6DS3TR_C_FIFO_6k66Hz; + break; + + default: + *val = LSM6DS3TR_C_FIFO_RATE_ND; + break; + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_DEN_functionality + * @brief This section groups all the functions concerning DEN + * functionality. + * @{ + * + */ + +/** + * @brief DEN active level configuration.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_lh in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_polarity_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_lh_t val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + if(ret == 0) { + ctrl5_c.den_lh = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + } + + return ret; +} + +/** + * @brief DEN active level configuration.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of den_lh in reg CTRL5_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_polarity_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_lh_t* val) { + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL5_C, (uint8_t*)&ctrl5_c, 1); + + switch(ctrl5_c.den_lh) { + case LSM6DS3TR_C_DEN_ACT_LOW: + *val = LSM6DS3TR_C_DEN_ACT_LOW; + break; + + case LSM6DS3TR_C_DEN_ACT_HIGH: + *val = LSM6DS3TR_C_DEN_ACT_HIGH; + break; + + default: + *val = LSM6DS3TR_C_DEN_POL_ND; + break; + } + + return ret; +} + +/** + * @brief DEN functionality marking mode[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_mode in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_mode_t val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + if(ret == 0) { + ctrl6_c.den_mode = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + } + + return ret; +} + +/** + * @brief DEN functionality marking mode[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_mode in reg CTRL6_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_mode_t* val) { + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL6_C, (uint8_t*)&ctrl6_c, 1); + + switch(ctrl6_c.den_mode) { + case LSM6DS3TR_C_DEN_DISABLE: + *val = LSM6DS3TR_C_DEN_DISABLE; + break; + + case LSM6DS3TR_C_LEVEL_LETCHED: + *val = LSM6DS3TR_C_LEVEL_LETCHED; + break; + + case LSM6DS3TR_C_LEVEL_TRIGGER: + *val = LSM6DS3TR_C_LEVEL_TRIGGER; + break; + + case LSM6DS3TR_C_EDGE_TRIGGER: + *val = LSM6DS3TR_C_EDGE_TRIGGER; + break; + + default: + *val = LSM6DS3TR_C_DEN_MODE_ND; + break; + } + + return ret; +} + +/** + * @brief Extend DEN functionality to accelerometer sensor.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_xl_g in reg CTRL9_XL + * and den_xl_en in CTRL4_C. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_enable_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_xl_en_t val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.den_xl_g = (uint8_t)val & 0x01U; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ctrl4_c.den_xl_en = (uint8_t)val & 0x02U; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + } + } + } + + return ret; +} + +/** + * @brief Extend DEN functionality to accelerometer sensor. [get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of den_xl_g in reg CTRL9_XL + * and den_xl_en in CTRL4_C. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_enable_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_xl_en_t* val) { + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL4_C, (uint8_t*)&ctrl4_c, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + switch((ctrl4_c.den_xl_en << 1) + ctrl9_xl.den_xl_g) { + case LSM6DS3TR_C_STAMP_IN_GY_DATA: + *val = LSM6DS3TR_C_STAMP_IN_GY_DATA; + break; + + case LSM6DS3TR_C_STAMP_IN_XL_DATA: + *val = LSM6DS3TR_C_STAMP_IN_XL_DATA; + break; + + case LSM6DS3TR_C_STAMP_IN_GY_XL_DATA: + *val = LSM6DS3TR_C_STAMP_IN_GY_XL_DATA; + break; + + default: + *val = LSM6DS3TR_C_DEN_STAMP_ND; + break; + } + } + + return ret; +} + +/** + * @brief DEN value stored in LSB of Z-axis.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_z in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_z_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.den_z = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + } + + return ret; +} + +/** + * @brief DEN value stored in LSB of Z-axis.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_z in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_z_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + *val = ctrl9_xl.den_z; + + return ret; +} + +/** + * @brief DEN value stored in LSB of Y-axis.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_y in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_y_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.den_y = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + } + + return ret; +} + +/** + * @brief DEN value stored in LSB of Y-axis.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_y in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_y_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + *val = ctrl9_xl.den_y; + + return ret; +} + +/** + * @brief DEN value stored in LSB of X-axis.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_x in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_x_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.den_x = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + } + + return ret; +} + +/** + * @brief DEN value stored in LSB of X-axis.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of den_x in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_den_mark_axis_x_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + *val = ctrl9_xl.den_x; + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Pedometer + * @brief This section groups all the functions that manage pedometer. + * @{ + * + */ + +/** + * @brief Reset pedometer step counter.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_rst_step in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_step_reset_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.pedo_rst_step = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief Reset pedometer step counter.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_rst_step in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_step_reset_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.pedo_rst_step; + + return ret; +} + +/** + * @brief Enable pedometer algorithm.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_sens_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.pedo_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + } + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief pedo_sens: Enable pedometer algorithm.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_sens_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.pedo_en; + + return ret; +} + +/** + * @brief Minimum threshold to detect a peak. Default is 10h.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ths_min in reg + * CONFIG_PEDO_THS_MIN + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_threshold_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + config_pedo_ths_min.ths_min = val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Minimum threshold to detect a peak. Default is 10h.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of ths_min in reg CONFIG_PEDO_THS_MIN + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_threshold_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + *val = config_pedo_ths_min.ths_min; + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief pedo_full_scale: Pedometer data range.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pedo_fs in + * reg CONFIG_PEDO_THS_MIN + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pedo_fs_t val) { + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + config_pedo_ths_min.pedo_fs = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Pedometer data range.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of pedo_fs in + * reg CONFIG_PEDO_THS_MIN + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pedo_fs_t* val) { + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_CONFIG_PEDO_THS_MIN, (uint8_t*)&config_pedo_ths_min, 1); + + if(ret == 0) { + switch(config_pedo_ths_min.pedo_fs) { + case LSM6DS3TR_C_PEDO_AT_2g: + *val = LSM6DS3TR_C_PEDO_AT_2g; + break; + + case LSM6DS3TR_C_PEDO_AT_4g: + *val = LSM6DS3TR_C_PEDO_AT_4g; + break; + + default: + *val = LSM6DS3TR_C_PEDO_FS_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Pedometer debounce configuration register (r/w).[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of deb_step in reg PEDO_DEB_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_debounce_steps_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + pedo_deb_reg.deb_step = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Pedometer debounce configuration register (r/w).[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of deb_step in reg PEDO_DEB_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_debounce_steps_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + *val = pedo_deb_reg.deb_step; + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Debounce time. If the time between two consecutive steps is + * greater than DEB_TIME*80ms, the debouncer is reactivated. + * Default value: 01101[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of deb_time in reg PEDO_DEB_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_timeout_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + pedo_deb_reg.deb_time = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Debounce time. If the time between two consecutive steps is + * greater than DEB_TIME*80ms, the debouncer is reactivated. + * Default value: 01101[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of deb_time in reg PEDO_DEB_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_timeout_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_PEDO_DEB_REG, (uint8_t*)&pedo_deb_reg, 1); + + if(ret == 0) { + *val = pedo_deb_reg.deb_time; + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Time period register for step detection on delta time (r/w).[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_steps_period_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_STEP_COUNT_DELTA, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Time period register for step detection on delta time (r/w).[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_pedo_steps_period_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_STEP_COUNT_DELTA, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_significant_motion + * @brief This section groups all the functions that manage the + * significant motion detection. + * @{ + * + */ + +/** + * @brief Enable significant motion detection function.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sign_motion_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_motion_sens_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.sign_motion_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + } + + return ret; +} + +/** + * @brief Enable significant motion detection function.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of sign_motion_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_motion_sens_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.sign_motion_en; + + return ret; +} + +/** + * @brief Significant motion threshold.[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that store significant motion threshold. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_motion_threshold_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SM_THS, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Significant motion threshold.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that store significant motion threshold. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_motion_threshold_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SM_THS, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_tilt_detection + * @brief This section groups all the functions that manage the tilt + * event detection. + * @{ + * + */ + +/** + * @brief Enable tilt calculation.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tilt_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_sens_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.tilt_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + } + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief Enable tilt calculation.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tilt_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_sens_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.tilt_en; + + return ret; +} + +/** + * @brief Enable tilt calculation.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tilt_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wrist_tilt_sens_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.wrist_tilt_en = val; + + if(val != 0x00U) { + ctrl10_c.func_en = val; + } + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief Enable tilt calculation.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tilt_en in reg CTRL10_C + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_wrist_tilt_sens_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + *val = ctrl10_c.wrist_tilt_en; + + return ret; +} + +/** + * @brief Absolute Wrist Tilt latency register (r/w). + * Absolute wrist tilt latency parameters. + * 1 LSB = 40 ms. Default value: 0Fh (600 ms).[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_latency_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_LAT, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt latency register (r/w). + * Absolute wrist tilt latency parameters. + * 1 LSB = 40 ms. Default value: 0Fh (600 ms).[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_latency_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_LAT, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt threshold register(r/w). + * Absolute wrist tilt threshold parameters. + * 1 LSB = 15.625 mg.Default value: 20h (500 mg).[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_threshold_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_THS, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt threshold register(r/w). + * Absolute wrist tilt threshold parameters. + * 1 LSB = 15.625 mg.Default value: 20h (500 mg).[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_threshold_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_THS, buff, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt mask register (r/w).[set] + * + * @param ctx Read / write interface definitions + * @param val Registers A_WRIST_TILT_MASK + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_src_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_a_wrist_tilt_mask_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_MASK, (uint8_t*)val, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Absolute Wrist Tilt mask register (r/w).[get] + * + * @param ctx Read / write interface definitions + * @param val Registers A_WRIST_TILT_MASK + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_tilt_src_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_a_wrist_tilt_mask_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_B); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_A_WRIST_TILT_MASK, (uint8_t*)val, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_ magnetometer_sensor + * @brief This section groups all the functions that manage additional + * magnetometer sensor. + * @{ + * + */ + +/** + * @brief Enable soft-iron correction algorithm for magnetometer.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of soft_en in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_soft_iron_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + + if(ret == 0) { + ctrl9_xl.soft_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + } + + return ret; +} + +/** + * @brief Enable soft-iron correction algorithm for magnetometer.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of soft_en in reg CTRL9_XL + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_soft_iron_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL9_XL, (uint8_t*)&ctrl9_xl, 1); + *val = ctrl9_xl.soft_en; + + return ret; +} + +/** + * @brief Enable hard-iron correction algorithm for magnetometer.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of iron_en in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_hard_iron_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_config_t master_config; + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.iron_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + if(val != 0x00U) { + ctrl10_c.func_en = val; + } + + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + } + } + + return ret; +} + +/** + * @brief Enable hard-iron correction algorithm for magnetometer.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of iron_en in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_hard_iron_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + *val = master_config.iron_en; + + return ret; +} + +/** + * @brief Soft iron 3x3 matrix. Value are expressed in sign-module format. + * (Es. SVVVVVVVb where S is the sign 0/+1/- and V is the value).[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_soft_iron_mat_set(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MAG_SI_XX, buff, 9); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Soft iron 3x3 matrix. Value are expressed in sign-module format. + * (Es. SVVVVVVVb where S is the sign 0/+1/- and V is the value).[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_soft_iron_mat_get(stmdev_ctx_t* ctx, uint8_t* buff) { + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MAG_SI_XX, buff, 9); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Offset for hard-iron compensation register (r/w). The value is + * expressed as a 16-bit word in two’s complement.[set] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that contains data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_offset_set(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + buff[1] = (uint8_t)((uint16_t)val[0] / 256U); + buff[0] = (uint8_t)((uint16_t)val[0] - (buff[1] * 256U)); + buff[3] = (uint8_t)((uint16_t)val[1] / 256U); + buff[2] = (uint8_t)((uint16_t)val[1] - (buff[3] * 256U)); + buff[5] = (uint8_t)((uint16_t)val[2] / 256U); + buff[4] = (uint8_t)((uint16_t)val[2] - (buff[5] * 256U)); + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MAG_OFFX_L, buff, 6); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Offset for hard-iron compensation register(r/w). + * The value is expressed as a 16-bit word in two’s complement.[get] + * + * @param ctx Read / write interface definitions + * @param buff Buffer that stores data read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_mag_offset_get(stmdev_ctx_t* ctx, int16_t* val) { + uint8_t buff[6]; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MAG_OFFX_L, buff, 6); + + if(ret == 0) { + val[0] = (int16_t)buff[1]; + val[0] = (val[0] * 256) + (int16_t)buff[0]; + val[1] = (int16_t)buff[3]; + val[1] = (val[1] * 256) + (int16_t)buff[2]; + val[2] = (int16_t)buff[5]; + val[2] = (val[2] * 256) + (int16_t)buff[4]; + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @defgroup LSM6DS3TR_C_Sensor_hub + * @brief This section groups all the functions that manage the sensor + * hub functionality. + * @{ + * + */ + +/** + * @brief Enable function.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values func_en + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_func_en_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + + if(ret == 0) { + ctrl10_c.func_en = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_CTRL10_C, (uint8_t*)&ctrl10_c, 1); + } + + return ret; +} + +/** + * @brief Sensor synchronization time frame with the step of 500 ms and + * full range of 5s. Unsigned 8-bit.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tph in reg SENSOR_SYNC_TIME_FRAME + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_sync_sens_frame_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_sensor_sync_time_frame_t sensor_sync_time_frame; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_TIME_FRAME, (uint8_t*)&sensor_sync_time_frame, 1); + + if(ret == 0) { + sensor_sync_time_frame.tph = val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_TIME_FRAME, (uint8_t*)&sensor_sync_time_frame, 1); + } + + return ret; +} + +/** + * @brief Sensor synchronization time frame with the step of 500 ms and + * full range of 5s. Unsigned 8-bit.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of tph in reg SENSOR_SYNC_TIME_FRAME + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_sync_sens_frame_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_sensor_sync_time_frame_t sensor_sync_time_frame; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_TIME_FRAME, (uint8_t*)&sensor_sync_time_frame, 1); + *val = sensor_sync_time_frame.tph; + + return ret; +} + +/** + * @brief Resolution ratio of error code for sensor synchronization.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of rr in reg SENSOR_SYNC_RES_RATIO + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_sync_sens_ratio_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rr_t val) { + lsm6ds3tr_c_sensor_sync_res_ratio_t sensor_sync_res_ratio; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_RES_RATIO, (uint8_t*)&sensor_sync_res_ratio, 1); + + if(ret == 0) { + sensor_sync_res_ratio.rr = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_RES_RATIO, (uint8_t*)&sensor_sync_res_ratio, 1); + } + + return ret; +} + +/** + * @brief Resolution ratio of error code for sensor synchronization.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of rr in reg SENSOR_SYNC_RES_RATIO + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_sync_sens_ratio_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rr_t* val) { + lsm6ds3tr_c_sensor_sync_res_ratio_t sensor_sync_res_ratio; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSOR_SYNC_RES_RATIO, (uint8_t*)&sensor_sync_res_ratio, 1); + + switch(sensor_sync_res_ratio.rr) { + case LSM6DS3TR_C_RES_RATIO_2_11: + *val = LSM6DS3TR_C_RES_RATIO_2_11; + break; + + case LSM6DS3TR_C_RES_RATIO_2_12: + *val = LSM6DS3TR_C_RES_RATIO_2_12; + break; + + case LSM6DS3TR_C_RES_RATIO_2_13: + *val = LSM6DS3TR_C_RES_RATIO_2_13; + break; + + case LSM6DS3TR_C_RES_RATIO_2_14: + *val = LSM6DS3TR_C_RES_RATIO_2_14; + break; + + default: + *val = LSM6DS3TR_C_RES_RATIO_ND; + break; + } + + return ret; +} + +/** + * @brief Sensor hub I2C master enable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of master_on in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_master_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.master_on = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief Sensor hub I2C master enable.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of master_on in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_master_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + *val = master_config.master_on; + + return ret; +} + +/** + * @brief I2C interface pass-through.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pass_through_mode in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_pass_through_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.pass_through_mode = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief I2C interface pass-through.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pass_through_mode in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_pass_through_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + *val = master_config.pass_through_mode; + + return ret; +} + +/** + * @brief Master I2C pull-up enable/disable.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of pull_up_en in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_pin_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pull_up_en_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.pull_up_en = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief Master I2C pull-up enable/disable.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of pull_up_en in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_pin_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pull_up_en_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + switch(master_config.pull_up_en) { + case LSM6DS3TR_C_EXT_PULL_UP: + *val = LSM6DS3TR_C_EXT_PULL_UP; + break; + + case LSM6DS3TR_C_INTERNAL_PULL_UP: + *val = LSM6DS3TR_C_INTERNAL_PULL_UP; + break; + + default: + *val = LSM6DS3TR_C_SH_PIN_MODE; + break; + } + + return ret; +} + +/** + * @brief Sensor hub trigger signal selection.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of start_config in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_syncro_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_start_config_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.start_config = (uint8_t)val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief Sensor hub trigger signal selection.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of start_config in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_syncro_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_start_config_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + switch(master_config.start_config) { + case LSM6DS3TR_C_XL_GY_DRDY: + *val = LSM6DS3TR_C_XL_GY_DRDY; + break; + + case LSM6DS3TR_C_EXT_ON_INT2_PIN: + *val = LSM6DS3TR_C_EXT_ON_INT2_PIN; + break; + + default: + *val = LSM6DS3TR_C_SH_SYNCRO_ND; + break; + } + + return ret; +} + +/** + * @brief Manage the Master DRDY signal on INT1 pad.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_on_int1 in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_drdy_on_int1_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + + if(ret == 0) { + master_config.drdy_on_int1 = val; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + } + + return ret; +} + +/** + * @brief Manage the Master DRDY signal on INT1 pad.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of drdy_on_int1 in reg MASTER_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_drdy_on_int1_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_config_t master_config; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CONFIG, (uint8_t*)&master_config, 1); + *val = master_config.drdy_on_int1; + + return ret; +} + +/** + * @brief Sensor hub output registers.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure of registers from SENSORHUB1_REG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_read_data_raw_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_emb_sh_read_t* val) { + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SENSORHUB1_REG, (uint8_t*)&(val->sh_byte_1), 12); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENSORHUB13_REG, (uint8_t*)&(val->sh_byte_13), 6); + } + + return ret; +} + +/** + * @brief Master command code used for stamping for sensor sync.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of master_cmd_code in + * reg MASTER_CMD_CODE + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_cmd_sens_sync_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_master_cmd_code_t master_cmd_code; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CMD_CODE, (uint8_t*)&master_cmd_code, 1); + + if(ret == 0) { + master_cmd_code.master_cmd_code = val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_MASTER_CMD_CODE, (uint8_t*)&master_cmd_code, 1); + } + + return ret; +} + +/** + * @brief Master command code used for stamping for sensor sync.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of master_cmd_code in + * reg MASTER_CMD_CODE + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_cmd_sens_sync_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_master_cmd_code_t master_cmd_code; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_MASTER_CMD_CODE, (uint8_t*)&master_cmd_code, 1); + *val = master_cmd_code.master_cmd_code; + + return ret; +} + +/** + * @brief Error code used for sensor synchronization.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of error_code in + * reg SENS_SYNC_SPI_ERROR_CODE. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_spi_sync_error_set(stmdev_ctx_t* ctx, uint8_t val) { + lsm6ds3tr_c_sens_sync_spi_error_code_t sens_sync_spi_error_code; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE, (uint8_t*)&sens_sync_spi_error_code, 1); + + if(ret == 0) { + sens_sync_spi_error_code.error_code = val; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE, (uint8_t*)&sens_sync_spi_error_code, 1); + } + + return ret; +} + +/** + * @brief Error code used for sensor synchronization.[get] + * + * @param ctx Read / write interface definitions + * @param val Change the values of error_code in + * reg SENS_SYNC_SPI_ERROR_CODE. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_spi_sync_error_get(stmdev_ctx_t* ctx, uint8_t* val) { + lsm6ds3tr_c_sens_sync_spi_error_code_t sens_sync_spi_error_code; + int32_t ret; + + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE, (uint8_t*)&sens_sync_spi_error_code, 1); + *val = sens_sync_spi_error_code.error_code; + + return ret; +} + +/** + * @brief Number of external sensors to be read by the sensor hub.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of aux_sens_on in reg SLAVE0_CONFIG. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_num_of_dev_connected_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_aux_sens_on_t val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + slave0_config.aux_sens_on = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Number of external sensors to be read by the sensor hub.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of aux_sens_on in reg SLAVE0_CONFIG. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t + lsm6ds3tr_c_sh_num_of_dev_connected_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_aux_sens_on_t* val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + switch(slave0_config.aux_sens_on) { + case LSM6DS3TR_C_SLV_0: + *val = LSM6DS3TR_C_SLV_0; + break; + + case LSM6DS3TR_C_SLV_0_1: + *val = LSM6DS3TR_C_SLV_0_1; + break; + + case LSM6DS3TR_C_SLV_0_1_2: + *val = LSM6DS3TR_C_SLV_0_1_2; + break; + + case LSM6DS3TR_C_SLV_0_1_2_3: + *val = LSM6DS3TR_C_SLV_0_1_2_3; + break; + + default: + *val = LSM6DS3TR_C_SLV_EN_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Configure slave 0 for perform a write.[set] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_data; 8 bit data to write + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_cfg_write(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_write_t* val) { + lsm6ds3tr_c_slv0_add_t slv0_add; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv0_add.slave0_add = val->slv0_add; + slv0_add.rw_0 = 0; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV0_ADD, (uint8_t*)&slv0_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV0_SUBADD, &(val->slv0_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_DATAWRITE_SRC_MODE_SUB_SLV0, &(val->slv0_data), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + + return ret; +} + +/** + * @brief Configure slave 0 for perform a read.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_len; num of bit to read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slv0_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + lsm6ds3tr_c_slv0_add_t slv0_add; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv0_add.slave0_add = val->slv_add; + slv0_add.rw_0 = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV0_ADD, (uint8_t*)&slv0_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV0_SUBADD, &(val->slv_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + slave0_config.slave0_numop = val->slv_len; + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + } + + return ret; +} + +/** + * @brief Configure slave 1 for perform a read.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_len; num of bit to read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slv1_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + lsm6ds3tr_c_slv1_add_t slv1_add; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv1_add.slave1_add = val->slv_add; + slv1_add.r_1 = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV1_ADD, (uint8_t*)&slv1_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV1_SUBADD, &(val->slv_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + slave1_config.slave1_numop = val->slv_len; + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + } + + return ret; +} + +/** + * @brief Configure slave 2 for perform a read.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_len; num of bit to read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slv2_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val) { + lsm6ds3tr_c_slv2_add_t slv2_add; + lsm6ds3tr_c_slave2_config_t slave2_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv2_add.slave2_add = val->slv_add; + slv2_add.r_2 = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV2_ADD, (uint8_t*)&slv2_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV2_SUBADD, &(val->slv_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + slave2_config.slave2_numop = val->slv_len; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + } + + return ret; +} + +/** + * @brief Configure slave 3 for perform a read.[get] + * + * @param ctx Read / write interface definitions + * @param val Structure that contain: + * - uint8_t slv_add; 8 bit i2c device address + * - uint8_t slv_subadd; 8 bit register device address + * - uint8_t slv_len; num of bit to read + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slv3_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val) { + lsm6ds3tr_c_slave3_config_t slave3_config; + lsm6ds3tr_c_slv3_add_t slv3_add; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + slv3_add.slave3_add = val->slv_add; + slv3_add.r_3 = 1; + ret = lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLV3_ADD, (uint8_t*)&slv3_add, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLV3_SUBADD, (uint8_t*)&(val->slv_subadd), 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg( + ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + + if(ret == 0) { + slave3_config.slave3_numop = val->slv_len; + ret = lsm6ds3tr_c_write_reg( + ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 0 starting from the + * sensor hub trigger.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slave0_rate in reg SLAVE0_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_0_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave0_rate_t val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + slave0_config.slave0_rate = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 0 starting from the + * sensor hub trigger.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of slave0_rate in reg SLAVE0_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_0_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave0_rate_t* val) { + lsm6ds3tr_c_slave0_config_t slave0_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE0_CONFIG, (uint8_t*)&slave0_config, 1); + + if(ret == 0) { + switch(slave0_config.slave0_rate) { + case LSM6DS3TR_C_SL0_NO_DEC: + *val = LSM6DS3TR_C_SL0_NO_DEC; + break; + + case LSM6DS3TR_C_SL0_DEC_2: + *val = LSM6DS3TR_C_SL0_DEC_2; + break; + + case LSM6DS3TR_C_SL0_DEC_4: + *val = LSM6DS3TR_C_SL0_DEC_4; + break; + + case LSM6DS3TR_C_SL0_DEC_8: + *val = LSM6DS3TR_C_SL0_DEC_8; + break; + + default: + *val = LSM6DS3TR_C_SL0_DEC_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Slave 0 write operation is performed only at the first sensor + * hub cycle. + * This is effective if the Aux_sens_on[1:0] field in + * SLAVE0_CONFIG(04h) is set to a value other than 00.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of write_once in reg SLAVE1_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_write_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_write_once_t val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + slave1_config.write_once = (uint8_t)val; + + if(ret == 0) { + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Slave 0 write operation is performed only at the first sensor + * hub cycle. + * This is effective if the Aux_sens_on[1:0] field in + * SLAVE0_CONFIG(04h) is set to a value other than 00.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of write_once in reg SLAVE1_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_write_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_write_once_t* val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + switch(slave1_config.write_once) { + case LSM6DS3TR_C_EACH_SH_CYCLE: + *val = LSM6DS3TR_C_EACH_SH_CYCLE; + break; + + case LSM6DS3TR_C_ONLY_FIRST_CYCLE: + *val = LSM6DS3TR_C_ONLY_FIRST_CYCLE; + break; + + default: + *val = LSM6DS3TR_C_SH_WR_MODE_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 1 starting from the + * sensor hub trigger.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slave1_rate in reg SLAVE1_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_1_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave1_rate_t val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + slave1_config.slave1_rate = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 1 starting from the + * sensor hub trigger.[get] + * + * @param ctx Read / write interface definitions reg SLAVE1_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_1_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave1_rate_t* val) { + lsm6ds3tr_c_slave1_config_t slave1_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE1_CONFIG, (uint8_t*)&slave1_config, 1); + + if(ret == 0) { + switch(slave1_config.slave1_rate) { + case LSM6DS3TR_C_SL1_NO_DEC: + *val = LSM6DS3TR_C_SL1_NO_DEC; + break; + + case LSM6DS3TR_C_SL1_DEC_2: + *val = LSM6DS3TR_C_SL1_DEC_2; + break; + + case LSM6DS3TR_C_SL1_DEC_4: + *val = LSM6DS3TR_C_SL1_DEC_4; + break; + + case LSM6DS3TR_C_SL1_DEC_8: + *val = LSM6DS3TR_C_SL1_DEC_8; + break; + + default: + *val = LSM6DS3TR_C_SL1_DEC_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 2 starting from the + * sensor hub trigger.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slave2_rate in reg SLAVE2_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_2_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave2_rate_t val) { + lsm6ds3tr_c_slave2_config_t slave2_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + slave2_config.slave2_rate = (uint8_t)val; + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 2 starting from the + * sensor hub trigger.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of slave2_rate in reg SLAVE2_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_2_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave2_rate_t* val) { + lsm6ds3tr_c_slave2_config_t slave2_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE2_CONFIG, (uint8_t*)&slave2_config, 1); + + if(ret == 0) { + switch(slave2_config.slave2_rate) { + case LSM6DS3TR_C_SL2_NO_DEC: + *val = LSM6DS3TR_C_SL2_NO_DEC; + break; + + case LSM6DS3TR_C_SL2_DEC_2: + *val = LSM6DS3TR_C_SL2_DEC_2; + break; + + case LSM6DS3TR_C_SL2_DEC_4: + *val = LSM6DS3TR_C_SL2_DEC_4; + break; + + case LSM6DS3TR_C_SL2_DEC_8: + *val = LSM6DS3TR_C_SL2_DEC_8; + break; + + default: + *val = LSM6DS3TR_C_SL2_DEC_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 3 starting from the + * sensor hub trigger.[set] + * + * @param ctx Read / write interface definitions + * @param val Change the values of slave3_rate in reg SLAVE3_CONFIG + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_3_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave3_rate_t val) { + lsm6ds3tr_c_slave3_config_t slave3_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + slave3_config.slave3_rate = (uint8_t)val; + + if(ret == 0) { + ret = + lsm6ds3tr_c_write_reg(ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + + if(ret == 0) { + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + } + + return ret; +} + +/** + * @brief Decimation of read operation on Slave 3 starting from the + * sensor hub trigger.[get] + * + * @param ctx Read / write interface definitions + * @param val Get the values of slave3_rate in reg SLAVE3_CONFIG. + * @retval Interface status (MANDATORY: return 0 -> no Error). + * + */ +int32_t lsm6ds3tr_c_sh_slave_3_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave3_rate_t* val) { + lsm6ds3tr_c_slave3_config_t slave3_config; + int32_t ret; + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_BANK_A); + + if(ret == 0) { + ret = lsm6ds3tr_c_read_reg(ctx, LSM6DS3TR_C_SLAVE3_CONFIG, (uint8_t*)&slave3_config, 1); + + if(ret == 0) { + switch(slave3_config.slave3_rate) { + case LSM6DS3TR_C_SL3_NO_DEC: + *val = LSM6DS3TR_C_SL3_NO_DEC; + break; + + case LSM6DS3TR_C_SL3_DEC_2: + *val = LSM6DS3TR_C_SL3_DEC_2; + break; + + case LSM6DS3TR_C_SL3_DEC_4: + *val = LSM6DS3TR_C_SL3_DEC_4; + break; + + case LSM6DS3TR_C_SL3_DEC_8: + *val = LSM6DS3TR_C_SL3_DEC_8; + break; + + default: + *val = LSM6DS3TR_C_SL3_DEC_ND; + break; + } + + ret = lsm6ds3tr_c_mem_bank_set(ctx, LSM6DS3TR_C_USER_BANK); + } + } + + return ret; +} + +/** + * @} + * + */ + +/** + * @} + * + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/lsm6ds3tr_c_reg.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/lsm6ds3tr_c_reg.h new file mode 100644 index 000000000..8cb592c0d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/imu/lsm6ds3tr_c_reg.h @@ -0,0 +1,2448 @@ +/** + ****************************************************************************** + * @file lsm6ds3tr_c_reg.h + * @author Sensors Software Solution Team + * @brief This file contains all the functions prototypes for the + * lsm6ds3tr_c_reg.c driver. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2021 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef LSM6DS3TR_C_DRIVER_H +#define LSM6DS3TR_C_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include +#include +#include + +/** @addtogroup LSM6DS3TR_C + * @{ + * + */ + +/** @defgroup Endianness definitions + * @{ + * + */ + +#ifndef DRV_BYTE_ORDER +#ifndef __BYTE_ORDER__ + +#define DRV_LITTLE_ENDIAN 1234 +#define DRV_BIG_ENDIAN 4321 + +/** if _BYTE_ORDER is not defined, choose the endianness of your architecture + * by uncommenting the define which fits your platform endianness + */ +//#define DRV_BYTE_ORDER DRV_BIG_ENDIAN +#define DRV_BYTE_ORDER DRV_LITTLE_ENDIAN + +#else /* defined __BYTE_ORDER__ */ + +#define DRV_LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +#define DRV_BIG_ENDIAN __ORDER_BIG_ENDIAN__ +#define DRV_BYTE_ORDER __BYTE_ORDER__ + +#endif /* __BYTE_ORDER__*/ +#endif /* DRV_BYTE_ORDER */ + +/** + * @} + * + */ + +/** @defgroup STMicroelectronics sensors common types + * @{ + * + */ + +#ifndef MEMS_SHARED_TYPES +#define MEMS_SHARED_TYPES + +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} bitwise_t; + +#define PROPERTY_DISABLE (0U) +#define PROPERTY_ENABLE (1U) + +/** @addtogroup Interfaces_Functions + * @brief This section provide a set of functions used to read and + * write a generic register of the device. + * MANDATORY: return 0 -> no Error. + * @{ + * + */ + +typedef int32_t (*stmdev_write_ptr)(void*, uint8_t, const uint8_t*, uint16_t); +typedef int32_t (*stmdev_read_ptr)(void*, uint8_t, uint8_t*, uint16_t); +typedef void (*stmdev_mdelay_ptr)(uint32_t millisec); + +typedef struct { + /** Component mandatory fields **/ + stmdev_write_ptr write_reg; + stmdev_read_ptr read_reg; + /** Component optional fields **/ + stmdev_mdelay_ptr mdelay; + /** Customizable optional pointer **/ + void* handle; +} stmdev_ctx_t; + +/** + * @} + * + */ + +#endif /* MEMS_SHARED_TYPES */ + +#ifndef MEMS_UCF_SHARED_TYPES +#define MEMS_UCF_SHARED_TYPES + +/** @defgroup Generic address-data structure definition + * @brief This structure is useful to load a predefined configuration + * of a sensor. + * You can create a sensor configuration by your own or using + * Unico / Unicleo tools available on STMicroelectronics + * web site. + * + * @{ + * + */ + +typedef struct { + uint8_t address; + uint8_t data; +} ucf_line_t; + +/** + * @} + * + */ + +#endif /* MEMS_UCF_SHARED_TYPES */ + +/** + * @} + * + */ + +/** @defgroup LSM6DS3TR_C_Infos + * @{ + * + */ + +/** I2C Device Address 8 bit format if SA0=0 -> D5 if SA0=1 -> D7 **/ +#define LSM6DS3TR_C_I2C_ADD_L 0xD5U +#define LSM6DS3TR_C_I2C_ADD_H 0xD7U + +/** Device Identification (Who am I) **/ +#define LSM6DS3TR_C_ID 0x6AU + +/** + * @} + * + */ + +#define LSM6DS3TR_C_FUNC_CFG_ACCESS 0x01U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 5; + uint8_t func_cfg_en : 3; /* func_cfg_en + func_cfg_en_b */ +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t func_cfg_en : 3; /* func_cfg_en + func_cfg_en_b */ + uint8_t not_used_01 : 5; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_func_cfg_access_t; + +#define LSM6DS3TR_C_SENSOR_SYNC_TIME_FRAME 0x04U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t tph : 4; + uint8_t not_used_01 : 4; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 4; + uint8_t tph : 4; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensor_sync_time_frame_t; + +#define LSM6DS3TR_C_SENSOR_SYNC_RES_RATIO 0x05U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t rr : 2; + uint8_t not_used_01 : 6; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 6; + uint8_t rr : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensor_sync_res_ratio_t; + +#define LSM6DS3TR_C_FIFO_CTRL1 0x06U +typedef struct { + uint8_t fth : 8; /* + FIFO_CTRL2(fth) */ +} lsm6ds3tr_c_fifo_ctrl1_t; + +#define LSM6DS3TR_C_FIFO_CTRL2 0x07U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t fth : 3; /* + FIFO_CTRL1(fth) */ + uint8_t fifo_temp_en : 1; + uint8_t not_used_01 : 2; + uint8_t timer_pedo_fifo_drdy : 1; + uint8_t timer_pedo_fifo_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t timer_pedo_fifo_en : 1; + uint8_t timer_pedo_fifo_drdy : 1; + uint8_t not_used_01 : 2; + uint8_t fifo_temp_en : 1; + uint8_t fth : 3; /* + FIFO_CTRL1(fth) */ +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_ctrl2_t; + +#define LSM6DS3TR_C_FIFO_CTRL3 0x08U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t dec_fifo_xl : 3; + uint8_t dec_fifo_gyro : 3; + uint8_t not_used_01 : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 2; + uint8_t dec_fifo_gyro : 3; + uint8_t dec_fifo_xl : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_ctrl3_t; + +#define LSM6DS3TR_C_FIFO_CTRL4 0x09U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t dec_ds3_fifo : 3; + uint8_t dec_ds4_fifo : 3; + uint8_t only_high_data : 1; + uint8_t stop_on_fth : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t stop_on_fth : 1; + uint8_t only_high_data : 1; + uint8_t dec_ds4_fifo : 3; + uint8_t dec_ds3_fifo : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_ctrl4_t; + +#define LSM6DS3TR_C_FIFO_CTRL5 0x0AU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t fifo_mode : 3; + uint8_t odr_fifo : 4; + uint8_t not_used_01 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 1; + uint8_t odr_fifo : 4; + uint8_t fifo_mode : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_ctrl5_t; + +#define LSM6DS3TR_C_DRDY_PULSE_CFG_G 0x0BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int2_wrist_tilt : 1; + uint8_t not_used_01 : 6; + uint8_t drdy_pulsed : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t drdy_pulsed : 1; + uint8_t not_used_01 : 6; + uint8_t int2_wrist_tilt : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_drdy_pulse_cfg_g_t; + +#define LSM6DS3TR_C_INT1_CTRL 0x0DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int1_drdy_xl : 1; + uint8_t int1_drdy_g : 1; + uint8_t int1_boot : 1; + uint8_t int1_fth : 1; + uint8_t int1_fifo_ovr : 1; + uint8_t int1_full_flag : 1; + uint8_t int1_sign_mot : 1; + uint8_t int1_step_detector : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t int1_step_detector : 1; + uint8_t int1_sign_mot : 1; + uint8_t int1_full_flag : 1; + uint8_t int1_fifo_ovr : 1; + uint8_t int1_fth : 1; + uint8_t int1_boot : 1; + uint8_t int1_drdy_g : 1; + uint8_t int1_drdy_xl : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_int1_ctrl_t; + +#define LSM6DS3TR_C_INT2_CTRL 0x0EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int2_drdy_xl : 1; + uint8_t int2_drdy_g : 1; + uint8_t int2_drdy_temp : 1; + uint8_t int2_fth : 1; + uint8_t int2_fifo_ovr : 1; + uint8_t int2_full_flag : 1; + uint8_t int2_step_count_ov : 1; + uint8_t int2_step_delta : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t int2_step_delta : 1; + uint8_t int2_step_count_ov : 1; + uint8_t int2_full_flag : 1; + uint8_t int2_fifo_ovr : 1; + uint8_t int2_fth : 1; + uint8_t int2_drdy_temp : 1; + uint8_t int2_drdy_g : 1; + uint8_t int2_drdy_xl : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_int2_ctrl_t; + +#define LSM6DS3TR_C_WHO_AM_I 0x0FU +#define LSM6DS3TR_C_CTRL1_XL 0x10U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bw0_xl : 1; + uint8_t lpf1_bw_sel : 1; + uint8_t fs_xl : 2; + uint8_t odr_xl : 4; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t odr_xl : 4; + uint8_t fs_xl : 2; + uint8_t lpf1_bw_sel : 1; + uint8_t bw0_xl : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl1_xl_t; + +#define LSM6DS3TR_C_CTRL2_G 0x11U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 1; + uint8_t fs_g : 3; /* fs_g + fs_125 */ + uint8_t odr_g : 4; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t odr_g : 4; + uint8_t fs_g : 3; /* fs_g + fs_125 */ + uint8_t not_used_01 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl2_g_t; + +#define LSM6DS3TR_C_CTRL3_C 0x12U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t sw_reset : 1; + uint8_t ble : 1; + uint8_t if_inc : 1; + uint8_t sim : 1; + uint8_t pp_od : 1; + uint8_t h_lactive : 1; + uint8_t bdu : 1; + uint8_t boot : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t boot : 1; + uint8_t bdu : 1; + uint8_t h_lactive : 1; + uint8_t pp_od : 1; + uint8_t sim : 1; + uint8_t if_inc : 1; + uint8_t ble : 1; + uint8_t sw_reset : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl3_c_t; + +#define LSM6DS3TR_C_CTRL4_C 0x13U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 1; + uint8_t lpf1_sel_g : 1; + uint8_t i2c_disable : 1; + uint8_t drdy_mask : 1; + uint8_t den_drdy_int1 : 1; + uint8_t int2_on_int1 : 1; + uint8_t sleep : 1; + uint8_t den_xl_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t den_xl_en : 1; + uint8_t sleep : 1; + uint8_t int2_on_int1 : 1; + uint8_t den_drdy_int1 : 1; + uint8_t drdy_mask : 1; + uint8_t i2c_disable : 1; + uint8_t lpf1_sel_g : 1; + uint8_t not_used_01 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl4_c_t; + +#define LSM6DS3TR_C_CTRL5_C 0x14U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t st_xl : 2; + uint8_t st_g : 2; + uint8_t den_lh : 1; + uint8_t rounding : 3; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t rounding : 3; + uint8_t den_lh : 1; + uint8_t st_g : 2; + uint8_t st_xl : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl5_c_t; + +#define LSM6DS3TR_C_CTRL6_C 0x15U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t ftype : 2; + uint8_t not_used_01 : 1; + uint8_t usr_off_w : 1; + uint8_t xl_hm_mode : 1; + uint8_t den_mode : 3; /* trig_en + lvl_en + lvl2_en */ +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t den_mode : 3; /* trig_en + lvl_en + lvl2_en */ + uint8_t xl_hm_mode : 1; + uint8_t usr_off_w : 1; + uint8_t not_used_01 : 1; + uint8_t ftype : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl6_c_t; + +#define LSM6DS3TR_C_CTRL7_G 0x16U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 2; + uint8_t rounding_status : 1; + uint8_t not_used_02 : 1; + uint8_t hpm_g : 2; + uint8_t hp_en_g : 1; + uint8_t g_hm_mode : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t g_hm_mode : 1; + uint8_t hp_en_g : 1; + uint8_t hpm_g : 2; + uint8_t not_used_02 : 1; + uint8_t rounding_status : 1; + uint8_t not_used_01 : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl7_g_t; + +#define LSM6DS3TR_C_CTRL8_XL 0x17U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t low_pass_on_6d : 1; + uint8_t not_used_01 : 1; + uint8_t hp_slope_xl_en : 1; + uint8_t input_composite : 1; + uint8_t hp_ref_mode : 1; + uint8_t hpcf_xl : 2; + uint8_t lpf2_xl_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t lpf2_xl_en : 1; + uint8_t hpcf_xl : 2; + uint8_t hp_ref_mode : 1; + uint8_t input_composite : 1; + uint8_t hp_slope_xl_en : 1; + uint8_t not_used_01 : 1; + uint8_t low_pass_on_6d : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl8_xl_t; + +#define LSM6DS3TR_C_CTRL9_XL 0x18U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 2; + uint8_t soft_en : 1; + uint8_t not_used_02 : 1; + uint8_t den_xl_g : 1; + uint8_t den_z : 1; + uint8_t den_y : 1; + uint8_t den_x : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t den_x : 1; + uint8_t den_y : 1; + uint8_t den_z : 1; + uint8_t den_xl_g : 1; + uint8_t not_used_02 : 1; + uint8_t soft_en : 1; + uint8_t not_used_01 : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl9_xl_t; + +#define LSM6DS3TR_C_CTRL10_C 0x19U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t sign_motion_en : 1; + uint8_t pedo_rst_step : 1; + uint8_t func_en : 1; + uint8_t tilt_en : 1; + uint8_t pedo_en : 1; + uint8_t timer_en : 1; + uint8_t not_used_01 : 1; + uint8_t wrist_tilt_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t wrist_tilt_en : 1; + uint8_t not_used_01 : 1; + uint8_t timer_en : 1; + uint8_t pedo_en : 1; + uint8_t tilt_en : 1; + uint8_t func_en : 1; + uint8_t pedo_rst_step : 1; + uint8_t sign_motion_en : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_ctrl10_c_t; + +#define LSM6DS3TR_C_MASTER_CONFIG 0x1AU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t master_on : 1; + uint8_t iron_en : 1; + uint8_t pass_through_mode : 1; + uint8_t pull_up_en : 1; + uint8_t start_config : 1; + uint8_t not_used_01 : 1; + uint8_t data_valid_sel_fifo : 1; + uint8_t drdy_on_int1 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t drdy_on_int1 : 1; + uint8_t data_valid_sel_fifo : 1; + uint8_t not_used_01 : 1; + uint8_t start_config : 1; + uint8_t pull_up_en : 1; + uint8_t pass_through_mode : 1; + uint8_t iron_en : 1; + uint8_t master_on : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_master_config_t; + +#define LSM6DS3TR_C_WAKE_UP_SRC 0x1BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t z_wu : 1; + uint8_t y_wu : 1; + uint8_t x_wu : 1; + uint8_t wu_ia : 1; + uint8_t sleep_state_ia : 1; + uint8_t ff_ia : 1; + uint8_t not_used_01 : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 2; + uint8_t ff_ia : 1; + uint8_t sleep_state_ia : 1; + uint8_t wu_ia : 1; + uint8_t x_wu : 1; + uint8_t y_wu : 1; + uint8_t z_wu : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_wake_up_src_t; + +#define LSM6DS3TR_C_TAP_SRC 0x1CU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t z_tap : 1; + uint8_t y_tap : 1; + uint8_t x_tap : 1; + uint8_t tap_sign : 1; + uint8_t double_tap : 1; + uint8_t single_tap : 1; + uint8_t tap_ia : 1; + uint8_t not_used_01 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 1; + uint8_t tap_ia : 1; + uint8_t single_tap : 1; + uint8_t double_tap : 1; + uint8_t tap_sign : 1; + uint8_t x_tap : 1; + uint8_t y_tap : 1; + uint8_t z_tap : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_tap_src_t; + +#define LSM6DS3TR_C_D6D_SRC 0x1DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t xl : 1; + uint8_t xh : 1; + uint8_t yl : 1; + uint8_t yh : 1; + uint8_t zl : 1; + uint8_t zh : 1; + uint8_t d6d_ia : 1; + uint8_t den_drdy : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t den_drdy : 1; + uint8_t d6d_ia : 1; + uint8_t zh : 1; + uint8_t zl : 1; + uint8_t yh : 1; + uint8_t yl : 1; + uint8_t xh : 1; + uint8_t xl : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_d6d_src_t; + +#define LSM6DS3TR_C_STATUS_REG 0x1EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t xlda : 1; + uint8_t gda : 1; + uint8_t tda : 1; + uint8_t not_used_01 : 5; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 5; + uint8_t tda : 1; + uint8_t gda : 1; + uint8_t xlda : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_status_reg_t; + +#define LSM6DS3TR_C_OUT_TEMP_L 0x20U +#define LSM6DS3TR_C_OUT_TEMP_H 0x21U +#define LSM6DS3TR_C_OUTX_L_G 0x22U +#define LSM6DS3TR_C_OUTX_H_G 0x23U +#define LSM6DS3TR_C_OUTY_L_G 0x24U +#define LSM6DS3TR_C_OUTY_H_G 0x25U +#define LSM6DS3TR_C_OUTZ_L_G 0x26U +#define LSM6DS3TR_C_OUTZ_H_G 0x27U +#define LSM6DS3TR_C_OUTX_L_XL 0x28U +#define LSM6DS3TR_C_OUTX_H_XL 0x29U +#define LSM6DS3TR_C_OUTY_L_XL 0x2AU +#define LSM6DS3TR_C_OUTY_H_XL 0x2BU +#define LSM6DS3TR_C_OUTZ_L_XL 0x2CU +#define LSM6DS3TR_C_OUTZ_H_XL 0x2DU +#define LSM6DS3TR_C_SENSORHUB1_REG 0x2EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub1_reg_t; + +#define LSM6DS3TR_C_SENSORHUB2_REG 0x2FU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub2_reg_t; + +#define LSM6DS3TR_C_SENSORHUB3_REG 0x30U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub3_reg_t; + +#define LSM6DS3TR_C_SENSORHUB4_REG 0x31U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub4_reg_t; + +#define LSM6DS3TR_C_SENSORHUB5_REG 0x32U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub5_reg_t; + +#define LSM6DS3TR_C_SENSORHUB6_REG 0x33U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub6_reg_t; + +#define LSM6DS3TR_C_SENSORHUB7_REG 0x34U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub7_reg_t; + +#define LSM6DS3TR_C_SENSORHUB8_REG 0x35U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub8_reg_t; + +#define LSM6DS3TR_C_SENSORHUB9_REG 0x36U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub9_reg_t; + +#define LSM6DS3TR_C_SENSORHUB10_REG 0x37U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub10_reg_t; + +#define LSM6DS3TR_C_SENSORHUB11_REG 0x38U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub11_reg_t; + +#define LSM6DS3TR_C_SENSORHUB12_REG 0x39U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub12_reg_t; + +#define LSM6DS3TR_C_FIFO_STATUS1 0x3AU +typedef struct { + uint8_t diff_fifo : 8; /* + FIFO_STATUS2(diff_fifo) */ +} lsm6ds3tr_c_fifo_status1_t; + +#define LSM6DS3TR_C_FIFO_STATUS2 0x3BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t diff_fifo : 3; /* + FIFO_STATUS1(diff_fifo) */ + uint8_t not_used_01 : 1; + uint8_t fifo_empty : 1; + uint8_t fifo_full_smart : 1; + uint8_t over_run : 1; + uint8_t waterm : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t waterm : 1; + uint8_t over_run : 1; + uint8_t fifo_full_smart : 1; + uint8_t fifo_empty : 1; + uint8_t not_used_01 : 1; + uint8_t diff_fifo : 3; /* + FIFO_STATUS1(diff_fifo) */ +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_status2_t; + +#define LSM6DS3TR_C_FIFO_STATUS3 0x3CU +typedef struct { + uint8_t fifo_pattern : 8; /* + FIFO_STATUS4(fifo_pattern) */ +} lsm6ds3tr_c_fifo_status3_t; + +#define LSM6DS3TR_C_FIFO_STATUS4 0x3DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t fifo_pattern : 2; /* + FIFO_STATUS3(fifo_pattern) */ + uint8_t not_used_01 : 6; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_01 : 6; + uint8_t fifo_pattern : 2; /* + FIFO_STATUS3(fifo_pattern) */ +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_fifo_status4_t; + +#define LSM6DS3TR_C_FIFO_DATA_OUT_L 0x3EU +#define LSM6DS3TR_C_FIFO_DATA_OUT_H 0x3FU +#define LSM6DS3TR_C_TIMESTAMP0_REG 0x40U +#define LSM6DS3TR_C_TIMESTAMP1_REG 0x41U +#define LSM6DS3TR_C_TIMESTAMP2_REG 0x42U +#define LSM6DS3TR_C_STEP_TIMESTAMP_L 0x49U +#define LSM6DS3TR_C_STEP_TIMESTAMP_H 0x4AU +#define LSM6DS3TR_C_STEP_COUNTER_L 0x4BU +#define LSM6DS3TR_C_STEP_COUNTER_H 0x4CU + +#define LSM6DS3TR_C_SENSORHUB13_REG 0x4DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub13_reg_t; + +#define LSM6DS3TR_C_SENSORHUB14_REG 0x4EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub14_reg_t; + +#define LSM6DS3TR_C_SENSORHUB15_REG 0x4FU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub15_reg_t; + +#define LSM6DS3TR_C_SENSORHUB16_REG 0x50U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub16_reg_t; + +#define LSM6DS3TR_C_SENSORHUB17_REG 0x51U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub17_reg_t; + +#define LSM6DS3TR_C_SENSORHUB18_REG 0x52U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t bit0 : 1; + uint8_t bit1 : 1; + uint8_t bit2 : 1; + uint8_t bit3 : 1; + uint8_t bit4 : 1; + uint8_t bit5 : 1; + uint8_t bit6 : 1; + uint8_t bit7 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t bit7 : 1; + uint8_t bit6 : 1; + uint8_t bit5 : 1; + uint8_t bit4 : 1; + uint8_t bit3 : 1; + uint8_t bit2 : 1; + uint8_t bit1 : 1; + uint8_t bit0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_sensorhub18_reg_t; + +#define LSM6DS3TR_C_FUNC_SRC1 0x53U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t sensorhub_end_op : 1; + uint8_t si_end_op : 1; + uint8_t hi_fail : 1; + uint8_t step_overflow : 1; + uint8_t step_detected : 1; + uint8_t tilt_ia : 1; + uint8_t sign_motion_ia : 1; + uint8_t step_count_delta_ia : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t step_count_delta_ia : 1; + uint8_t sign_motion_ia : 1; + uint8_t tilt_ia : 1; + uint8_t step_detected : 1; + uint8_t step_overflow : 1; + uint8_t hi_fail : 1; + uint8_t si_end_op : 1; + uint8_t sensorhub_end_op : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_func_src1_t; + +#define LSM6DS3TR_C_FUNC_SRC2 0x54U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t wrist_tilt_ia : 1; + uint8_t not_used_01 : 2; + uint8_t slave0_nack : 1; + uint8_t slave1_nack : 1; + uint8_t slave2_nack : 1; + uint8_t slave3_nack : 1; + uint8_t not_used_02 : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t not_used_02 : 1; + uint8_t slave3_nack : 1; + uint8_t slave2_nack : 1; + uint8_t slave1_nack : 1; + uint8_t slave0_nack : 1; + uint8_t not_used_01 : 2; + uint8_t wrist_tilt_ia : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_func_src2_t; + +#define LSM6DS3TR_C_WRIST_TILT_IA 0x55U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 2; + uint8_t wrist_tilt_ia_zneg : 1; + uint8_t wrist_tilt_ia_zpos : 1; + uint8_t wrist_tilt_ia_yneg : 1; + uint8_t wrist_tilt_ia_ypos : 1; + uint8_t wrist_tilt_ia_xneg : 1; + uint8_t wrist_tilt_ia_xpos : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t wrist_tilt_ia_xpos : 1; + uint8_t wrist_tilt_ia_xneg : 1; + uint8_t wrist_tilt_ia_ypos : 1; + uint8_t wrist_tilt_ia_yneg : 1; + uint8_t wrist_tilt_ia_zpos : 1; + uint8_t wrist_tilt_ia_zneg : 1; + uint8_t not_used_01 : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_wrist_tilt_ia_t; + +#define LSM6DS3TR_C_TAP_CFG 0x58U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t lir : 1; + uint8_t tap_z_en : 1; + uint8_t tap_y_en : 1; + uint8_t tap_x_en : 1; + uint8_t slope_fds : 1; + uint8_t inact_en : 2; + uint8_t interrupts_enable : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t interrupts_enable : 1; + uint8_t inact_en : 2; + uint8_t slope_fds : 1; + uint8_t tap_x_en : 1; + uint8_t tap_y_en : 1; + uint8_t tap_z_en : 1; + uint8_t lir : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_tap_cfg_t; + +#define LSM6DS3TR_C_TAP_THS_6D 0x59U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t tap_ths : 5; + uint8_t sixd_ths : 2; + uint8_t d4d_en : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t d4d_en : 1; + uint8_t sixd_ths : 2; + uint8_t tap_ths : 5; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_tap_ths_6d_t; + +#define LSM6DS3TR_C_INT_DUR2 0x5AU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t shock : 2; + uint8_t quiet : 2; + uint8_t dur : 4; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t dur : 4; + uint8_t quiet : 2; + uint8_t shock : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_int_dur2_t; + +#define LSM6DS3TR_C_WAKE_UP_THS 0x5BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t wk_ths : 6; + uint8_t not_used_01 : 1; + uint8_t single_double_tap : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t single_double_tap : 1; + uint8_t not_used_01 : 1; + uint8_t wk_ths : 6; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_wake_up_ths_t; + +#define LSM6DS3TR_C_WAKE_UP_DUR 0x5CU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t sleep_dur : 4; + uint8_t timer_hr : 1; + uint8_t wake_dur : 2; + uint8_t ff_dur : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t ff_dur : 1; + uint8_t wake_dur : 2; + uint8_t timer_hr : 1; + uint8_t sleep_dur : 4; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_wake_up_dur_t; + +#define LSM6DS3TR_C_FREE_FALL 0x5DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t ff_ths : 3; + uint8_t ff_dur : 5; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t ff_dur : 5; + uint8_t ff_ths : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_free_fall_t; + +#define LSM6DS3TR_C_MD1_CFG 0x5EU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int1_timer : 1; + uint8_t int1_tilt : 1; + uint8_t int1_6d : 1; + uint8_t int1_double_tap : 1; + uint8_t int1_ff : 1; + uint8_t int1_wu : 1; + uint8_t int1_single_tap : 1; + uint8_t int1_inact_state : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t int1_inact_state : 1; + uint8_t int1_single_tap : 1; + uint8_t int1_wu : 1; + uint8_t int1_ff : 1; + uint8_t int1_double_tap : 1; + uint8_t int1_6d : 1; + uint8_t int1_tilt : 1; + uint8_t int1_timer : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_md1_cfg_t; + +#define LSM6DS3TR_C_MD2_CFG 0x5FU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t int2_iron : 1; + uint8_t int2_tilt : 1; + uint8_t int2_6d : 1; + uint8_t int2_double_tap : 1; + uint8_t int2_ff : 1; + uint8_t int2_wu : 1; + uint8_t int2_single_tap : 1; + uint8_t int2_inact_state : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t int2_inact_state : 1; + uint8_t int2_single_tap : 1; + uint8_t int2_wu : 1; + uint8_t int2_ff : 1; + uint8_t int2_double_tap : 1; + uint8_t int2_6d : 1; + uint8_t int2_tilt : 1; + uint8_t int2_iron : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_md2_cfg_t; + +#define LSM6DS3TR_C_MASTER_CMD_CODE 0x60U +typedef struct { + uint8_t master_cmd_code : 8; +} lsm6ds3tr_c_master_cmd_code_t; + +#define LSM6DS3TR_C_SENS_SYNC_SPI_ERROR_CODE 0x61U +typedef struct { + uint8_t error_code : 8; +} lsm6ds3tr_c_sens_sync_spi_error_code_t; + +#define LSM6DS3TR_C_OUT_MAG_RAW_X_L 0x66U +#define LSM6DS3TR_C_OUT_MAG_RAW_X_H 0x67U +#define LSM6DS3TR_C_OUT_MAG_RAW_Y_L 0x68U +#define LSM6DS3TR_C_OUT_MAG_RAW_Y_H 0x69U +#define LSM6DS3TR_C_OUT_MAG_RAW_Z_L 0x6AU +#define LSM6DS3TR_C_OUT_MAG_RAW_Z_H 0x6BU +#define LSM6DS3TR_C_X_OFS_USR 0x73U +#define LSM6DS3TR_C_Y_OFS_USR 0x74U +#define LSM6DS3TR_C_Z_OFS_USR 0x75U +#define LSM6DS3TR_C_SLV0_ADD 0x02U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t rw_0 : 1; + uint8_t slave0_add : 7; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave0_add : 7; + uint8_t rw_0 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slv0_add_t; + +#define LSM6DS3TR_C_SLV0_SUBADD 0x03U +typedef struct { + uint8_t slave0_reg : 8; +} lsm6ds3tr_c_slv0_subadd_t; + +#define LSM6DS3TR_C_SLAVE0_CONFIG 0x04U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t slave0_numop : 3; + uint8_t src_mode : 1; + uint8_t aux_sens_on : 2; + uint8_t slave0_rate : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave0_rate : 2; + uint8_t aux_sens_on : 2; + uint8_t src_mode : 1; + uint8_t slave0_numop : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slave0_config_t; + +#define LSM6DS3TR_C_SLV1_ADD 0x05U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t r_1 : 1; + uint8_t slave1_add : 7; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave1_add : 7; + uint8_t r_1 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slv1_add_t; + +#define LSM6DS3TR_C_SLV1_SUBADD 0x06U +typedef struct { + uint8_t slave1_reg : 8; +} lsm6ds3tr_c_slv1_subadd_t; + +#define LSM6DS3TR_C_SLAVE1_CONFIG 0x07U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t slave1_numop : 3; + uint8_t not_used_01 : 2; + uint8_t write_once : 1; + uint8_t slave1_rate : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave1_rate : 2; + uint8_t write_once : 1; + uint8_t not_used_01 : 2; + uint8_t slave1_numop : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slave1_config_t; + +#define LSM6DS3TR_C_SLV2_ADD 0x08U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t r_2 : 1; + uint8_t slave2_add : 7; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave2_add : 7; + uint8_t r_2 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slv2_add_t; + +#define LSM6DS3TR_C_SLV2_SUBADD 0x09U +typedef struct { + uint8_t slave2_reg : 8; +} lsm6ds3tr_c_slv2_subadd_t; + +#define LSM6DS3TR_C_SLAVE2_CONFIG 0x0AU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t slave2_numop : 3; + uint8_t not_used_01 : 3; + uint8_t slave2_rate : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave2_rate : 2; + uint8_t not_used_01 : 3; + uint8_t slave2_numop : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slave2_config_t; + +#define LSM6DS3TR_C_SLV3_ADD 0x0BU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t r_3 : 1; + uint8_t slave3_add : 7; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave3_add : 7; + uint8_t r_3 : 1; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slv3_add_t; + +#define LSM6DS3TR_C_SLV3_SUBADD 0x0CU +typedef struct { + uint8_t slave3_reg : 8; +} lsm6ds3tr_c_slv3_subadd_t; + +#define LSM6DS3TR_C_SLAVE3_CONFIG 0x0DU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t slave3_numop : 3; + uint8_t not_used_01 : 3; + uint8_t slave3_rate : 2; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t slave3_rate : 2; + uint8_t not_used_01 : 3; + uint8_t slave3_numop : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_slave3_config_t; + +#define LSM6DS3TR_C_DATAWRITE_SRC_MODE_SUB_SLV0 0x0EU +typedef struct { + uint8_t slave_dataw : 8; +} lsm6ds3tr_c_datawrite_src_mode_sub_slv0_t; + +#define LSM6DS3TR_C_CONFIG_PEDO_THS_MIN 0x0FU +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t ths_min : 5; + uint8_t not_used_01 : 2; + uint8_t pedo_fs : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t pedo_fs : 1; + uint8_t not_used_01 : 2; + uint8_t ths_min : 5; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_config_pedo_ths_min_t; + +#define LSM6DS3TR_C_SM_THS 0x13U +#define LSM6DS3TR_C_PEDO_DEB_REG 0x14U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t deb_step : 3; + uint8_t deb_time : 5; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t deb_time : 5; + uint8_t deb_step : 3; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_pedo_deb_reg_t; + +#define LSM6DS3TR_C_STEP_COUNT_DELTA 0x15U +#define LSM6DS3TR_C_MAG_SI_XX 0x24U +#define LSM6DS3TR_C_MAG_SI_XY 0x25U +#define LSM6DS3TR_C_MAG_SI_XZ 0x26U +#define LSM6DS3TR_C_MAG_SI_YX 0x27U +#define LSM6DS3TR_C_MAG_SI_YY 0x28U +#define LSM6DS3TR_C_MAG_SI_YZ 0x29U +#define LSM6DS3TR_C_MAG_SI_ZX 0x2AU +#define LSM6DS3TR_C_MAG_SI_ZY 0x2BU +#define LSM6DS3TR_C_MAG_SI_ZZ 0x2CU +#define LSM6DS3TR_C_MAG_OFFX_L 0x2DU +#define LSM6DS3TR_C_MAG_OFFX_H 0x2EU +#define LSM6DS3TR_C_MAG_OFFY_L 0x2FU +#define LSM6DS3TR_C_MAG_OFFY_H 0x30U +#define LSM6DS3TR_C_MAG_OFFZ_L 0x31U +#define LSM6DS3TR_C_MAG_OFFZ_H 0x32U +#define LSM6DS3TR_C_A_WRIST_TILT_LAT 0x50U +#define LSM6DS3TR_C_A_WRIST_TILT_THS 0x54U +#define LSM6DS3TR_C_A_WRIST_TILT_MASK 0x59U +typedef struct { +#if DRV_BYTE_ORDER == DRV_LITTLE_ENDIAN + uint8_t not_used_01 : 2; + uint8_t wrist_tilt_mask_zneg : 1; + uint8_t wrist_tilt_mask_zpos : 1; + uint8_t wrist_tilt_mask_yneg : 1; + uint8_t wrist_tilt_mask_ypos : 1; + uint8_t wrist_tilt_mask_xneg : 1; + uint8_t wrist_tilt_mask_xpos : 1; +#elif DRV_BYTE_ORDER == DRV_BIG_ENDIAN + uint8_t wrist_tilt_mask_xpos : 1; + uint8_t wrist_tilt_mask_xneg : 1; + uint8_t wrist_tilt_mask_ypos : 1; + uint8_t wrist_tilt_mask_yneg : 1; + uint8_t wrist_tilt_mask_zpos : 1; + uint8_t wrist_tilt_mask_zneg : 1; + uint8_t not_used_01 : 2; +#endif /* DRV_BYTE_ORDER */ +} lsm6ds3tr_c_a_wrist_tilt_mask_t; + +/** + * @defgroup LSM6DS3TR_C_Register_Union + * @brief This union group all the registers having a bit-field + * description. + * This union is useful but it's not needed by the driver. + * + * REMOVING this union you are compliant with: + * MISRA-C 2012 [Rule 19.2] -> " Union are not allowed " + * + * @{ + * + */ +typedef union { + lsm6ds3tr_c_func_cfg_access_t func_cfg_access; + lsm6ds3tr_c_sensor_sync_time_frame_t sensor_sync_time_frame; + lsm6ds3tr_c_sensor_sync_res_ratio_t sensor_sync_res_ratio; + lsm6ds3tr_c_fifo_ctrl1_t fifo_ctrl1; + lsm6ds3tr_c_fifo_ctrl2_t fifo_ctrl2; + lsm6ds3tr_c_fifo_ctrl3_t fifo_ctrl3; + lsm6ds3tr_c_fifo_ctrl4_t fifo_ctrl4; + lsm6ds3tr_c_fifo_ctrl5_t fifo_ctrl5; + lsm6ds3tr_c_drdy_pulse_cfg_g_t drdy_pulse_cfg_g; + lsm6ds3tr_c_int1_ctrl_t int1_ctrl; + lsm6ds3tr_c_int2_ctrl_t int2_ctrl; + lsm6ds3tr_c_ctrl1_xl_t ctrl1_xl; + lsm6ds3tr_c_ctrl2_g_t ctrl2_g; + lsm6ds3tr_c_ctrl3_c_t ctrl3_c; + lsm6ds3tr_c_ctrl4_c_t ctrl4_c; + lsm6ds3tr_c_ctrl5_c_t ctrl5_c; + lsm6ds3tr_c_ctrl6_c_t ctrl6_c; + lsm6ds3tr_c_ctrl7_g_t ctrl7_g; + lsm6ds3tr_c_ctrl8_xl_t ctrl8_xl; + lsm6ds3tr_c_ctrl9_xl_t ctrl9_xl; + lsm6ds3tr_c_ctrl10_c_t ctrl10_c; + lsm6ds3tr_c_master_config_t master_config; + lsm6ds3tr_c_wake_up_src_t wake_up_src; + lsm6ds3tr_c_tap_src_t tap_src; + lsm6ds3tr_c_d6d_src_t d6d_src; + lsm6ds3tr_c_status_reg_t status_reg; + lsm6ds3tr_c_sensorhub1_reg_t sensorhub1_reg; + lsm6ds3tr_c_sensorhub2_reg_t sensorhub2_reg; + lsm6ds3tr_c_sensorhub3_reg_t sensorhub3_reg; + lsm6ds3tr_c_sensorhub4_reg_t sensorhub4_reg; + lsm6ds3tr_c_sensorhub5_reg_t sensorhub5_reg; + lsm6ds3tr_c_sensorhub6_reg_t sensorhub6_reg; + lsm6ds3tr_c_sensorhub7_reg_t sensorhub7_reg; + lsm6ds3tr_c_sensorhub8_reg_t sensorhub8_reg; + lsm6ds3tr_c_sensorhub9_reg_t sensorhub9_reg; + lsm6ds3tr_c_sensorhub10_reg_t sensorhub10_reg; + lsm6ds3tr_c_sensorhub11_reg_t sensorhub11_reg; + lsm6ds3tr_c_sensorhub12_reg_t sensorhub12_reg; + lsm6ds3tr_c_fifo_status1_t fifo_status1; + lsm6ds3tr_c_fifo_status2_t fifo_status2; + lsm6ds3tr_c_fifo_status3_t fifo_status3; + lsm6ds3tr_c_fifo_status4_t fifo_status4; + lsm6ds3tr_c_sensorhub13_reg_t sensorhub13_reg; + lsm6ds3tr_c_sensorhub14_reg_t sensorhub14_reg; + lsm6ds3tr_c_sensorhub15_reg_t sensorhub15_reg; + lsm6ds3tr_c_sensorhub16_reg_t sensorhub16_reg; + lsm6ds3tr_c_sensorhub17_reg_t sensorhub17_reg; + lsm6ds3tr_c_sensorhub18_reg_t sensorhub18_reg; + lsm6ds3tr_c_func_src1_t func_src1; + lsm6ds3tr_c_func_src2_t func_src2; + lsm6ds3tr_c_wrist_tilt_ia_t wrist_tilt_ia; + lsm6ds3tr_c_tap_cfg_t tap_cfg; + lsm6ds3tr_c_tap_ths_6d_t tap_ths_6d; + lsm6ds3tr_c_int_dur2_t int_dur2; + lsm6ds3tr_c_wake_up_ths_t wake_up_ths; + lsm6ds3tr_c_wake_up_dur_t wake_up_dur; + lsm6ds3tr_c_free_fall_t free_fall; + lsm6ds3tr_c_md1_cfg_t md1_cfg; + lsm6ds3tr_c_md2_cfg_t md2_cfg; + lsm6ds3tr_c_master_cmd_code_t master_cmd_code; + lsm6ds3tr_c_sens_sync_spi_error_code_t sens_sync_spi_error_code; + lsm6ds3tr_c_slv0_add_t slv0_add; + lsm6ds3tr_c_slv0_subadd_t slv0_subadd; + lsm6ds3tr_c_slave0_config_t slave0_config; + lsm6ds3tr_c_slv1_add_t slv1_add; + lsm6ds3tr_c_slv1_subadd_t slv1_subadd; + lsm6ds3tr_c_slave1_config_t slave1_config; + lsm6ds3tr_c_slv2_add_t slv2_add; + lsm6ds3tr_c_slv2_subadd_t slv2_subadd; + lsm6ds3tr_c_slave2_config_t slave2_config; + lsm6ds3tr_c_slv3_add_t slv3_add; + lsm6ds3tr_c_slv3_subadd_t slv3_subadd; + lsm6ds3tr_c_slave3_config_t slave3_config; + lsm6ds3tr_c_datawrite_src_mode_sub_slv0_t datawrite_src_mode_sub_slv0; + lsm6ds3tr_c_config_pedo_ths_min_t config_pedo_ths_min; + lsm6ds3tr_c_pedo_deb_reg_t pedo_deb_reg; + lsm6ds3tr_c_a_wrist_tilt_mask_t a_wrist_tilt_mask; + bitwise_t bitwise; + uint8_t byte; +} lsm6ds3tr_c_reg_t; + +/** + * @} + * + */ + +int32_t lsm6ds3tr_c_read_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, uint16_t len); +int32_t lsm6ds3tr_c_write_reg(stmdev_ctx_t* ctx, uint8_t reg, uint8_t* data, uint16_t len); + +float_t lsm6ds3tr_c_from_fs2g_to_mg(int16_t lsb); +float_t lsm6ds3tr_c_from_fs4g_to_mg(int16_t lsb); +float_t lsm6ds3tr_c_from_fs8g_to_mg(int16_t lsb); +float_t lsm6ds3tr_c_from_fs16g_to_mg(int16_t lsb); + +float_t lsm6ds3tr_c_from_fs125dps_to_mdps(int16_t lsb); +float_t lsm6ds3tr_c_from_fs250dps_to_mdps(int16_t lsb); +float_t lsm6ds3tr_c_from_fs500dps_to_mdps(int16_t lsb); +float_t lsm6ds3tr_c_from_fs1000dps_to_mdps(int16_t lsb); +float_t lsm6ds3tr_c_from_fs2000dps_to_mdps(int16_t lsb); + +float_t lsm6ds3tr_c_from_lsb_to_celsius(int16_t lsb); + +typedef enum { + LSM6DS3TR_C_2g = 0, + LSM6DS3TR_C_16g = 1, + LSM6DS3TR_C_4g = 2, + LSM6DS3TR_C_8g = 3, + LSM6DS3TR_C_XL_FS_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_fs_xl_t; +int32_t lsm6ds3tr_c_xl_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_xl_t val); +int32_t lsm6ds3tr_c_xl_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_xl_t* val); + +typedef enum { + LSM6DS3TR_C_XL_ODR_OFF = 0, + LSM6DS3TR_C_XL_ODR_12Hz5 = 1, + LSM6DS3TR_C_XL_ODR_26Hz = 2, + LSM6DS3TR_C_XL_ODR_52Hz = 3, + LSM6DS3TR_C_XL_ODR_104Hz = 4, + LSM6DS3TR_C_XL_ODR_208Hz = 5, + LSM6DS3TR_C_XL_ODR_416Hz = 6, + LSM6DS3TR_C_XL_ODR_833Hz = 7, + LSM6DS3TR_C_XL_ODR_1k66Hz = 8, + LSM6DS3TR_C_XL_ODR_3k33Hz = 9, + LSM6DS3TR_C_XL_ODR_6k66Hz = 10, + LSM6DS3TR_C_XL_ODR_1Hz6 = 11, + LSM6DS3TR_C_XL_ODR_ND = 12, /* ERROR CODE */ +} lsm6ds3tr_c_odr_xl_t; +int32_t lsm6ds3tr_c_xl_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_xl_t val); +int32_t lsm6ds3tr_c_xl_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_xl_t* val); + +typedef enum { + LSM6DS3TR_C_250dps = 0, + LSM6DS3TR_C_125dps = 1, + LSM6DS3TR_C_500dps = 2, + LSM6DS3TR_C_1000dps = 4, + LSM6DS3TR_C_2000dps = 6, + LSM6DS3TR_C_GY_FS_ND = 7, /* ERROR CODE */ +} lsm6ds3tr_c_fs_g_t; +int32_t lsm6ds3tr_c_gy_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_g_t val); +int32_t lsm6ds3tr_c_gy_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fs_g_t* val); + +typedef enum { + LSM6DS3TR_C_GY_ODR_OFF = 0, + LSM6DS3TR_C_GY_ODR_12Hz5 = 1, + LSM6DS3TR_C_GY_ODR_26Hz = 2, + LSM6DS3TR_C_GY_ODR_52Hz = 3, + LSM6DS3TR_C_GY_ODR_104Hz = 4, + LSM6DS3TR_C_GY_ODR_208Hz = 5, + LSM6DS3TR_C_GY_ODR_416Hz = 6, + LSM6DS3TR_C_GY_ODR_833Hz = 7, + LSM6DS3TR_C_GY_ODR_1k66Hz = 8, + LSM6DS3TR_C_GY_ODR_3k33Hz = 9, + LSM6DS3TR_C_GY_ODR_6k66Hz = 10, + LSM6DS3TR_C_GY_ODR_ND = 11, /* ERROR CODE */ +} lsm6ds3tr_c_odr_g_t; +int32_t lsm6ds3tr_c_gy_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_g_t val); +int32_t lsm6ds3tr_c_gy_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_g_t* val); + +int32_t lsm6ds3tr_c_block_data_update_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_block_data_update_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_LSb_1mg = 0, + LSM6DS3TR_C_LSb_16mg = 1, + LSM6DS3TR_C_WEIGHT_ND = 2, +} lsm6ds3tr_c_usr_off_w_t; +int32_t lsm6ds3tr_c_xl_offset_weight_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_usr_off_w_t val); +int32_t lsm6ds3tr_c_xl_offset_weight_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_usr_off_w_t* val); + +typedef enum { + LSM6DS3TR_C_XL_HIGH_PERFORMANCE = 0, + LSM6DS3TR_C_XL_NORMAL = 1, + LSM6DS3TR_C_XL_PW_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_xl_hm_mode_t; +int32_t lsm6ds3tr_c_xl_power_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_xl_hm_mode_t val); +int32_t lsm6ds3tr_c_xl_power_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_xl_hm_mode_t* val); + +typedef enum { + LSM6DS3TR_C_STAT_RND_DISABLE = 0, + LSM6DS3TR_C_STAT_RND_ENABLE = 1, + LSM6DS3TR_C_STAT_RND_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_rounding_status_t; +int32_t lsm6ds3tr_c_rounding_on_status_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_status_t val); +int32_t lsm6ds3tr_c_rounding_on_status_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_status_t* val); + +typedef enum { + LSM6DS3TR_C_GY_HIGH_PERFORMANCE = 0, + LSM6DS3TR_C_GY_NORMAL = 1, + LSM6DS3TR_C_GY_PW_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_g_hm_mode_t; +int32_t lsm6ds3tr_c_gy_power_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_g_hm_mode_t val); +int32_t lsm6ds3tr_c_gy_power_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_g_hm_mode_t* val); + +typedef struct { + lsm6ds3tr_c_wake_up_src_t wake_up_src; + lsm6ds3tr_c_tap_src_t tap_src; + lsm6ds3tr_c_d6d_src_t d6d_src; + lsm6ds3tr_c_status_reg_t status_reg; + lsm6ds3tr_c_func_src1_t func_src1; + lsm6ds3tr_c_func_src2_t func_src2; + lsm6ds3tr_c_wrist_tilt_ia_t wrist_tilt_ia; + lsm6ds3tr_c_a_wrist_tilt_mask_t a_wrist_tilt_mask; +} lsm6ds3tr_c_all_sources_t; +int32_t lsm6ds3tr_c_all_sources_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_all_sources_t* val); + +int32_t lsm6ds3tr_c_status_reg_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_status_reg_t* val); + +int32_t lsm6ds3tr_c_xl_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_gy_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_temp_flag_data_ready_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_xl_usr_offset_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_xl_usr_offset_get(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_timestamp_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_timestamp_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_LSB_6ms4 = 0, + LSM6DS3TR_C_LSB_25us = 1, + LSM6DS3TR_C_TS_RES_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_timer_hr_t; +int32_t lsm6ds3tr_c_timestamp_res_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_timer_hr_t val); +int32_t lsm6ds3tr_c_timestamp_res_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_timer_hr_t* val); + +typedef enum { + LSM6DS3TR_C_ROUND_DISABLE = 0, + LSM6DS3TR_C_ROUND_XL = 1, + LSM6DS3TR_C_ROUND_GY = 2, + LSM6DS3TR_C_ROUND_GY_XL = 3, + LSM6DS3TR_C_ROUND_SH1_TO_SH6 = 4, + LSM6DS3TR_C_ROUND_XL_SH1_TO_SH6 = 5, + LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH12 = 6, + LSM6DS3TR_C_ROUND_GY_XL_SH1_TO_SH6 = 7, + LSM6DS3TR_C_ROUND_OUT_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_rounding_t; +int32_t lsm6ds3tr_c_rounding_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_t val); +int32_t lsm6ds3tr_c_rounding_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rounding_t* val); + +int32_t lsm6ds3tr_c_temperature_raw_get(stmdev_ctx_t* ctx, int16_t* val); +int32_t lsm6ds3tr_c_angular_rate_raw_get(stmdev_ctx_t* ctx, int16_t* val); +int32_t lsm6ds3tr_c_acceleration_raw_get(stmdev_ctx_t* ctx, int16_t* val); + +int32_t lsm6ds3tr_c_mag_calibrated_raw_get(stmdev_ctx_t* ctx, int16_t* val); + +int32_t lsm6ds3tr_c_fifo_raw_data_get(stmdev_ctx_t* ctx, uint8_t* buffer, uint8_t len); + +typedef enum { + LSM6DS3TR_C_USER_BANK = 0, + LSM6DS3TR_C_BANK_A = 4, + LSM6DS3TR_C_BANK_B = 5, + LSM6DS3TR_C_BANK_ND = 6, /* ERROR CODE */ +} lsm6ds3tr_c_func_cfg_en_t; +int32_t lsm6ds3tr_c_mem_bank_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_func_cfg_en_t val); +int32_t lsm6ds3tr_c_mem_bank_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_func_cfg_en_t* val); + +typedef enum { + LSM6DS3TR_C_DRDY_LATCHED = 0, + LSM6DS3TR_C_DRDY_PULSED = 1, + LSM6DS3TR_C_DRDY_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_drdy_pulsed_g_t; +int32_t lsm6ds3tr_c_data_ready_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_drdy_pulsed_g_t val); +int32_t lsm6ds3tr_c_data_ready_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_drdy_pulsed_g_t* val); + +int32_t lsm6ds3tr_c_device_id_get(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_reset_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_reset_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_LSB_AT_LOW_ADD = 0, + LSM6DS3TR_C_MSB_AT_LOW_ADD = 1, + LSM6DS3TR_C_DATA_FMT_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_ble_t; +int32_t lsm6ds3tr_c_data_format_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_ble_t val); +int32_t lsm6ds3tr_c_data_format_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_ble_t* val); + +int32_t lsm6ds3tr_c_auto_increment_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_auto_increment_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_boot_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_boot_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_XL_ST_DISABLE = 0, + LSM6DS3TR_C_XL_ST_POSITIVE = 1, + LSM6DS3TR_C_XL_ST_NEGATIVE = 2, + LSM6DS3TR_C_XL_ST_ND = 3, /* ERROR CODE */ +} lsm6ds3tr_c_st_xl_t; +int32_t lsm6ds3tr_c_xl_self_test_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_xl_t val); +int32_t lsm6ds3tr_c_xl_self_test_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_xl_t* val); + +typedef enum { + LSM6DS3TR_C_GY_ST_DISABLE = 0, + LSM6DS3TR_C_GY_ST_POSITIVE = 1, + LSM6DS3TR_C_GY_ST_NEGATIVE = 3, + LSM6DS3TR_C_GY_ST_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_st_g_t; +int32_t lsm6ds3tr_c_gy_self_test_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_g_t val); +int32_t lsm6ds3tr_c_gy_self_test_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_st_g_t* val); + +int32_t lsm6ds3tr_c_filter_settling_mask_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_filter_settling_mask_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_USE_SLOPE = 0, + LSM6DS3TR_C_USE_HPF = 1, + LSM6DS3TR_C_HP_PATH_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_slope_fds_t; +int32_t lsm6ds3tr_c_xl_hp_path_internal_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slope_fds_t val); +int32_t lsm6ds3tr_c_xl_hp_path_internal_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slope_fds_t* val); + +typedef enum { + LSM6DS3TR_C_XL_ANA_BW_1k5Hz = 0, + LSM6DS3TR_C_XL_ANA_BW_400Hz = 1, + LSM6DS3TR_C_XL_ANA_BW_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_bw0_xl_t; +int32_t lsm6ds3tr_c_xl_filter_analog_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_bw0_xl_t val); +int32_t lsm6ds3tr_c_xl_filter_analog_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_bw0_xl_t* val); + +typedef enum { + LSM6DS3TR_C_XL_LP1_ODR_DIV_2 = 0, + LSM6DS3TR_C_XL_LP1_ODR_DIV_4 = 1, + LSM6DS3TR_C_XL_LP1_NA = 2, /* ERROR CODE */ +} lsm6ds3tr_c_lpf1_bw_sel_t; +int32_t lsm6ds3tr_c_xl_lp1_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_bw_sel_t val); +int32_t lsm6ds3tr_c_xl_lp1_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_bw_sel_t* val); + +typedef enum { + LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_50 = 0x00, + LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_100 = 0x01, + LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_9 = 0x02, + LSM6DS3TR_C_XL_LOW_LAT_LP_ODR_DIV_400 = 0x03, + LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_50 = 0x10, + LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_100 = 0x11, + LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_9 = 0x12, + LSM6DS3TR_C_XL_LOW_NOISE_LP_ODR_DIV_400 = 0x13, + LSM6DS3TR_C_XL_LP_NA = 0x20, /* ERROR CODE */ +} lsm6ds3tr_c_input_composite_t; +int32_t lsm6ds3tr_c_xl_lp2_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_input_composite_t val); +int32_t lsm6ds3tr_c_xl_lp2_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_input_composite_t* val); + +int32_t lsm6ds3tr_c_xl_reference_mode_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_xl_reference_mode_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_XL_HP_ODR_DIV_4 = 0x00, /* Slope filter */ + LSM6DS3TR_C_XL_HP_ODR_DIV_100 = 0x01, + LSM6DS3TR_C_XL_HP_ODR_DIV_9 = 0x02, + LSM6DS3TR_C_XL_HP_ODR_DIV_400 = 0x03, + LSM6DS3TR_C_XL_HP_NA = 0x10, /* ERROR CODE */ +} lsm6ds3tr_c_hpcf_xl_t; +int32_t lsm6ds3tr_c_xl_hp_bandwidth_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_hpcf_xl_t val); +int32_t lsm6ds3tr_c_xl_hp_bandwidth_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_hpcf_xl_t* val); + +typedef enum { + LSM6DS3TR_C_LP2_ONLY = 0x00, + + LSM6DS3TR_C_HP_16mHz_LP2 = 0x80, + LSM6DS3TR_C_HP_65mHz_LP2 = 0x90, + LSM6DS3TR_C_HP_260mHz_LP2 = 0xA0, + LSM6DS3TR_C_HP_1Hz04_LP2 = 0xB0, + + LSM6DS3TR_C_HP_DISABLE_LP1_LIGHT = 0x0A, + LSM6DS3TR_C_HP_DISABLE_LP1_NORMAL = 0x09, + LSM6DS3TR_C_HP_DISABLE_LP_STRONG = 0x08, + LSM6DS3TR_C_HP_DISABLE_LP1_AGGRESSIVE = 0x0B, + + LSM6DS3TR_C_HP_16mHz_LP1_LIGHT = 0x8A, + LSM6DS3TR_C_HP_65mHz_LP1_NORMAL = 0x99, + LSM6DS3TR_C_HP_260mHz_LP1_STRONG = 0xA8, + LSM6DS3TR_C_HP_1Hz04_LP1_AGGRESSIVE = 0xBB, + + LSM6DS3TR_C_HP_GY_BAND_NA = 0xFF, /* ERROR CODE */ +} lsm6ds3tr_c_lpf1_sel_g_t; +int32_t lsm6ds3tr_c_gy_band_pass_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_sel_g_t val); +int32_t lsm6ds3tr_c_gy_band_pass_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lpf1_sel_g_t* val); + +typedef enum { + LSM6DS3TR_C_SPI_4_WIRE = 0, + LSM6DS3TR_C_SPI_3_WIRE = 1, + LSM6DS3TR_C_SPI_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_sim_t; +int32_t lsm6ds3tr_c_spi_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_sim_t val); +int32_t lsm6ds3tr_c_spi_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_sim_t* val); + +typedef enum { + LSM6DS3TR_C_I2C_ENABLE = 0, + LSM6DS3TR_C_I2C_DISABLE = 1, + LSM6DS3TR_C_I2C_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_i2c_disable_t; +int32_t lsm6ds3tr_c_i2c_interface_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_i2c_disable_t val); +int32_t lsm6ds3tr_c_i2c_interface_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_i2c_disable_t* val); + +typedef struct { + uint8_t int1_drdy_xl : 1; + uint8_t int1_drdy_g : 1; + uint8_t int1_boot : 1; + uint8_t int1_fth : 1; + uint8_t int1_fifo_ovr : 1; + uint8_t int1_full_flag : 1; + uint8_t int1_sign_mot : 1; + uint8_t int1_step_detector : 1; + uint8_t int1_timer : 1; + uint8_t int1_tilt : 1; + uint8_t int1_6d : 1; + uint8_t int1_double_tap : 1; + uint8_t int1_ff : 1; + uint8_t int1_wu : 1; + uint8_t int1_single_tap : 1; + uint8_t int1_inact_state : 1; + uint8_t den_drdy_int1 : 1; + uint8_t drdy_on_int1 : 1; +} lsm6ds3tr_c_int1_route_t; +int32_t lsm6ds3tr_c_pin_int1_route_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_int1_route_t val); +int32_t lsm6ds3tr_c_pin_int1_route_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_int1_route_t* val); + +typedef struct { + uint8_t int2_drdy_xl : 1; + uint8_t int2_drdy_g : 1; + uint8_t int2_drdy_temp : 1; + uint8_t int2_fth : 1; + uint8_t int2_fifo_ovr : 1; + uint8_t int2_full_flag : 1; + uint8_t int2_step_count_ov : 1; + uint8_t int2_step_delta : 1; + uint8_t int2_iron : 1; + uint8_t int2_tilt : 1; + uint8_t int2_6d : 1; + uint8_t int2_double_tap : 1; + uint8_t int2_ff : 1; + uint8_t int2_wu : 1; + uint8_t int2_single_tap : 1; + uint8_t int2_inact_state : 1; + uint8_t int2_wrist_tilt : 1; +} lsm6ds3tr_c_int2_route_t; +int32_t lsm6ds3tr_c_pin_int2_route_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_int2_route_t val); +int32_t lsm6ds3tr_c_pin_int2_route_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_int2_route_t* val); + +typedef enum { + LSM6DS3TR_C_PUSH_PULL = 0, + LSM6DS3TR_C_OPEN_DRAIN = 1, + LSM6DS3TR_C_PIN_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_pp_od_t; +int32_t lsm6ds3tr_c_pin_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pp_od_t val); +int32_t lsm6ds3tr_c_pin_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pp_od_t* val); + +typedef enum { + LSM6DS3TR_C_ACTIVE_HIGH = 0, + LSM6DS3TR_C_ACTIVE_LOW = 1, + LSM6DS3TR_C_POLARITY_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_h_lactive_t; +int32_t lsm6ds3tr_c_pin_polarity_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_h_lactive_t val); +int32_t lsm6ds3tr_c_pin_polarity_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_h_lactive_t* val); + +int32_t lsm6ds3tr_c_all_on_int1_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_all_on_int1_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_INT_PULSED = 0, + LSM6DS3TR_C_INT_LATCHED = 1, + LSM6DS3TR_C_INT_MODE = 2, /* ERROR CODE */ +} lsm6ds3tr_c_lir_t; +int32_t lsm6ds3tr_c_int_notification_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_lir_t val); +int32_t lsm6ds3tr_c_int_notification_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_lir_t* val); + +int32_t lsm6ds3tr_c_wkup_threshold_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_wkup_threshold_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_wkup_dur_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_wkup_dur_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_gy_sleep_mode_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_gy_sleep_mode_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_PROPERTY_DISABLE = 0, + LSM6DS3TR_C_XL_12Hz5_GY_NOT_AFFECTED = 1, + LSM6DS3TR_C_XL_12Hz5_GY_SLEEP = 2, + LSM6DS3TR_C_XL_12Hz5_GY_PD = 3, + LSM6DS3TR_C_ACT_MODE_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_inact_en_t; +int32_t lsm6ds3tr_c_act_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_inact_en_t val); +int32_t lsm6ds3tr_c_act_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_inact_en_t* val); + +int32_t lsm6ds3tr_c_act_sleep_dur_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_act_sleep_dur_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_src_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_tap_src_t* val); + +int32_t lsm6ds3tr_c_tap_detection_on_z_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_detection_on_z_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_detection_on_y_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_detection_on_y_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_detection_on_x_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_detection_on_x_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_threshold_x_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_threshold_x_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_shock_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_shock_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_quiet_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_quiet_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tap_dur_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tap_dur_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_ONLY_SINGLE = 0, + LSM6DS3TR_C_BOTH_SINGLE_DOUBLE = 1, + LSM6DS3TR_C_TAP_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_single_double_tap_t; +int32_t lsm6ds3tr_c_tap_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_single_double_tap_t val); +int32_t lsm6ds3tr_c_tap_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_single_double_tap_t* val); + +typedef enum { + LSM6DS3TR_C_ODR_DIV_2_FEED = 0, + LSM6DS3TR_C_LPF2_FEED = 1, + LSM6DS3TR_C_6D_FEED_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_low_pass_on_6d_t; +int32_t lsm6ds3tr_c_6d_feed_data_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_low_pass_on_6d_t val); +int32_t lsm6ds3tr_c_6d_feed_data_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_low_pass_on_6d_t* val); + +typedef enum { + LSM6DS3TR_C_DEG_80 = 0, + LSM6DS3TR_C_DEG_70 = 1, + LSM6DS3TR_C_DEG_60 = 2, + LSM6DS3TR_C_DEG_50 = 3, + LSM6DS3TR_C_6D_TH_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_sixd_ths_t; +int32_t lsm6ds3tr_c_6d_threshold_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_sixd_ths_t val); +int32_t lsm6ds3tr_c_6d_threshold_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_sixd_ths_t* val); + +int32_t lsm6ds3tr_c_4d_mode_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_4d_mode_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_ff_dur_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_ff_dur_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_FF_TSH_156mg = 0, + LSM6DS3TR_C_FF_TSH_219mg = 1, + LSM6DS3TR_C_FF_TSH_250mg = 2, + LSM6DS3TR_C_FF_TSH_312mg = 3, + LSM6DS3TR_C_FF_TSH_344mg = 4, + LSM6DS3TR_C_FF_TSH_406mg = 5, + LSM6DS3TR_C_FF_TSH_469mg = 6, + LSM6DS3TR_C_FF_TSH_500mg = 7, + LSM6DS3TR_C_FF_TSH_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_ff_ths_t; +int32_t lsm6ds3tr_c_ff_threshold_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_ff_ths_t val); +int32_t lsm6ds3tr_c_ff_threshold_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_ff_ths_t* val); + +int32_t lsm6ds3tr_c_fifo_watermark_set(stmdev_ctx_t* ctx, uint16_t val); +int32_t lsm6ds3tr_c_fifo_watermark_get(stmdev_ctx_t* ctx, uint16_t* val); + +int32_t lsm6ds3tr_c_fifo_data_level_get(stmdev_ctx_t* ctx, uint16_t* val); + +int32_t lsm6ds3tr_c_fifo_wtm_flag_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_fifo_pattern_get(stmdev_ctx_t* ctx, uint16_t* val); + +int32_t lsm6ds3tr_c_fifo_temp_batch_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_fifo_temp_batch_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_TRG_XL_GY_DRDY = 0, + LSM6DS3TR_C_TRG_STEP_DETECT = 1, + LSM6DS3TR_C_TRG_SH_DRDY = 2, + LSM6DS3TR_C_TRG_SH_ND = 3, /* ERROR CODE */ +} lsm6ds3tr_c_trigger_fifo_t; +int32_t lsm6ds3tr_c_fifo_write_trigger_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_trigger_fifo_t val); +int32_t lsm6ds3tr_c_fifo_write_trigger_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_trigger_fifo_t* val); + +int32_t lsm6ds3tr_c_fifo_pedo_and_timestamp_batch_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_fifo_pedo_and_timestamp_batch_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_XL_DISABLE = 0, + LSM6DS3TR_C_FIFO_XL_NO_DEC = 1, + LSM6DS3TR_C_FIFO_XL_DEC_2 = 2, + LSM6DS3TR_C_FIFO_XL_DEC_3 = 3, + LSM6DS3TR_C_FIFO_XL_DEC_4 = 4, + LSM6DS3TR_C_FIFO_XL_DEC_8 = 5, + LSM6DS3TR_C_FIFO_XL_DEC_16 = 6, + LSM6DS3TR_C_FIFO_XL_DEC_32 = 7, + LSM6DS3TR_C_FIFO_XL_DEC_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_dec_fifo_xl_t; +int32_t lsm6ds3tr_c_fifo_xl_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_xl_t val); +int32_t lsm6ds3tr_c_fifo_xl_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_xl_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_GY_DISABLE = 0, + LSM6DS3TR_C_FIFO_GY_NO_DEC = 1, + LSM6DS3TR_C_FIFO_GY_DEC_2 = 2, + LSM6DS3TR_C_FIFO_GY_DEC_3 = 3, + LSM6DS3TR_C_FIFO_GY_DEC_4 = 4, + LSM6DS3TR_C_FIFO_GY_DEC_8 = 5, + LSM6DS3TR_C_FIFO_GY_DEC_16 = 6, + LSM6DS3TR_C_FIFO_GY_DEC_32 = 7, + LSM6DS3TR_C_FIFO_GY_DEC_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_dec_fifo_gyro_t; +int32_t lsm6ds3tr_c_fifo_gy_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_gyro_t val); +int32_t lsm6ds3tr_c_fifo_gy_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_fifo_gyro_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_DS3_DISABLE = 0, + LSM6DS3TR_C_FIFO_DS3_NO_DEC = 1, + LSM6DS3TR_C_FIFO_DS3_DEC_2 = 2, + LSM6DS3TR_C_FIFO_DS3_DEC_3 = 3, + LSM6DS3TR_C_FIFO_DS3_DEC_4 = 4, + LSM6DS3TR_C_FIFO_DS3_DEC_8 = 5, + LSM6DS3TR_C_FIFO_DS3_DEC_16 = 6, + LSM6DS3TR_C_FIFO_DS3_DEC_32 = 7, + LSM6DS3TR_C_FIFO_DS3_DEC_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_dec_ds3_fifo_t; +int32_t lsm6ds3tr_c_fifo_dataset_3_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds3_fifo_t val); +int32_t lsm6ds3tr_c_fifo_dataset_3_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds3_fifo_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_DS4_DISABLE = 0, + LSM6DS3TR_C_FIFO_DS4_NO_DEC = 1, + LSM6DS3TR_C_FIFO_DS4_DEC_2 = 2, + LSM6DS3TR_C_FIFO_DS4_DEC_3 = 3, + LSM6DS3TR_C_FIFO_DS4_DEC_4 = 4, + LSM6DS3TR_C_FIFO_DS4_DEC_8 = 5, + LSM6DS3TR_C_FIFO_DS4_DEC_16 = 6, + LSM6DS3TR_C_FIFO_DS4_DEC_32 = 7, + LSM6DS3TR_C_FIFO_DS4_DEC_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_dec_ds4_fifo_t; +int32_t lsm6ds3tr_c_fifo_dataset_4_batch_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds4_fifo_t val); +int32_t lsm6ds3tr_c_fifo_dataset_4_batch_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_dec_ds4_fifo_t* val); + +int32_t lsm6ds3tr_c_fifo_xl_gy_8bit_format_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_fifo_xl_gy_8bit_format_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_fifo_stop_on_wtm_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_fifo_stop_on_wtm_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_BYPASS_MODE = 0, + LSM6DS3TR_C_FIFO_MODE = 1, + LSM6DS3TR_C_STREAM_TO_FIFO_MODE = 3, + LSM6DS3TR_C_BYPASS_TO_STREAM_MODE = 4, + LSM6DS3TR_C_STREAM_MODE = 6, + LSM6DS3TR_C_FIFO_MODE_ND = 8, /* ERROR CODE */ +} lsm6ds3tr_c_fifo_mode_t; +int32_t lsm6ds3tr_c_fifo_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_fifo_mode_t val); +int32_t lsm6ds3tr_c_fifo_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_fifo_mode_t* val); + +typedef enum { + LSM6DS3TR_C_FIFO_DISABLE = 0, + LSM6DS3TR_C_FIFO_12Hz5 = 1, + LSM6DS3TR_C_FIFO_26Hz = 2, + LSM6DS3TR_C_FIFO_52Hz = 3, + LSM6DS3TR_C_FIFO_104Hz = 4, + LSM6DS3TR_C_FIFO_208Hz = 5, + LSM6DS3TR_C_FIFO_416Hz = 6, + LSM6DS3TR_C_FIFO_833Hz = 7, + LSM6DS3TR_C_FIFO_1k66Hz = 8, + LSM6DS3TR_C_FIFO_3k33Hz = 9, + LSM6DS3TR_C_FIFO_6k66Hz = 10, + LSM6DS3TR_C_FIFO_RATE_ND = 11, /* ERROR CODE */ +} lsm6ds3tr_c_odr_fifo_t; +int32_t lsm6ds3tr_c_fifo_data_rate_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_fifo_t val); +int32_t lsm6ds3tr_c_fifo_data_rate_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_odr_fifo_t* val); + +typedef enum { + LSM6DS3TR_C_DEN_ACT_LOW = 0, + LSM6DS3TR_C_DEN_ACT_HIGH = 1, + LSM6DS3TR_C_DEN_POL_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_den_lh_t; +int32_t lsm6ds3tr_c_den_polarity_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_lh_t val); +int32_t lsm6ds3tr_c_den_polarity_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_lh_t* val); + +typedef enum { + LSM6DS3TR_C_DEN_DISABLE = 0, + LSM6DS3TR_C_LEVEL_FIFO = 6, + LSM6DS3TR_C_LEVEL_LETCHED = 3, + LSM6DS3TR_C_LEVEL_TRIGGER = 2, + LSM6DS3TR_C_EDGE_TRIGGER = 4, + LSM6DS3TR_C_DEN_MODE_ND = 5, /* ERROR CODE */ +} lsm6ds3tr_c_den_mode_t; +int32_t lsm6ds3tr_c_den_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_mode_t val); +int32_t lsm6ds3tr_c_den_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_mode_t* val); + +typedef enum { + LSM6DS3TR_C_STAMP_IN_GY_DATA = 0, + LSM6DS3TR_C_STAMP_IN_XL_DATA = 1, + LSM6DS3TR_C_STAMP_IN_GY_XL_DATA = 2, + LSM6DS3TR_C_DEN_STAMP_ND = 3, /* ERROR CODE */ +} lsm6ds3tr_c_den_xl_en_t; +int32_t lsm6ds3tr_c_den_enable_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_xl_en_t val); +int32_t lsm6ds3tr_c_den_enable_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_den_xl_en_t* val); + +int32_t lsm6ds3tr_c_den_mark_axis_z_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_den_mark_axis_z_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_den_mark_axis_y_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_den_mark_axis_y_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_den_mark_axis_x_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_den_mark_axis_x_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_step_reset_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_step_reset_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_sens_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_sens_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_threshold_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_threshold_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_PEDO_AT_2g = 0, + LSM6DS3TR_C_PEDO_AT_4g = 1, + LSM6DS3TR_C_PEDO_FS_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_pedo_fs_t; +int32_t lsm6ds3tr_c_pedo_full_scale_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pedo_fs_t val); +int32_t lsm6ds3tr_c_pedo_full_scale_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pedo_fs_t* val); + +int32_t lsm6ds3tr_c_pedo_debounce_steps_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_debounce_steps_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_timeout_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_pedo_timeout_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_pedo_steps_period_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_pedo_steps_period_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_motion_sens_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_motion_sens_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_motion_threshold_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_motion_threshold_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_tilt_sens_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_tilt_sens_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_wrist_tilt_sens_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_wrist_tilt_sens_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_tilt_latency_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_tilt_latency_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_tilt_threshold_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_tilt_threshold_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_tilt_src_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_a_wrist_tilt_mask_t* val); +int32_t lsm6ds3tr_c_tilt_src_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_a_wrist_tilt_mask_t* val); + +int32_t lsm6ds3tr_c_mag_soft_iron_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_mag_soft_iron_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_mag_hard_iron_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_mag_hard_iron_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_mag_soft_iron_mat_set(stmdev_ctx_t* ctx, uint8_t* buff); +int32_t lsm6ds3tr_c_mag_soft_iron_mat_get(stmdev_ctx_t* ctx, uint8_t* buff); + +int32_t lsm6ds3tr_c_mag_offset_set(stmdev_ctx_t* ctx, int16_t* val); +int32_t lsm6ds3tr_c_mag_offset_get(stmdev_ctx_t* ctx, int16_t* val); + +int32_t lsm6ds3tr_c_func_en_set(stmdev_ctx_t* ctx, uint8_t val); + +int32_t lsm6ds3tr_c_sh_sync_sens_frame_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_sync_sens_frame_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_RES_RATIO_2_11 = 0, + LSM6DS3TR_C_RES_RATIO_2_12 = 1, + LSM6DS3TR_C_RES_RATIO_2_13 = 2, + LSM6DS3TR_C_RES_RATIO_2_14 = 3, + LSM6DS3TR_C_RES_RATIO_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_rr_t; +int32_t lsm6ds3tr_c_sh_sync_sens_ratio_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_rr_t val); +int32_t lsm6ds3tr_c_sh_sync_sens_ratio_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_rr_t* val); + +int32_t lsm6ds3tr_c_sh_master_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_master_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_sh_pass_through_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_pass_through_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_EXT_PULL_UP = 0, + LSM6DS3TR_C_INTERNAL_PULL_UP = 1, + LSM6DS3TR_C_SH_PIN_MODE = 2, /* ERROR CODE */ +} lsm6ds3tr_c_pull_up_en_t; +int32_t lsm6ds3tr_c_sh_pin_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_pull_up_en_t val); +int32_t lsm6ds3tr_c_sh_pin_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_pull_up_en_t* val); + +typedef enum { + LSM6DS3TR_C_XL_GY_DRDY = 0, + LSM6DS3TR_C_EXT_ON_INT2_PIN = 1, + LSM6DS3TR_C_SH_SYNCRO_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_start_config_t; +int32_t lsm6ds3tr_c_sh_syncro_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_start_config_t val); +int32_t lsm6ds3tr_c_sh_syncro_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_start_config_t* val); + +int32_t lsm6ds3tr_c_sh_drdy_on_int1_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_drdy_on_int1_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef struct { + lsm6ds3tr_c_sensorhub1_reg_t sh_byte_1; + lsm6ds3tr_c_sensorhub2_reg_t sh_byte_2; + lsm6ds3tr_c_sensorhub3_reg_t sh_byte_3; + lsm6ds3tr_c_sensorhub4_reg_t sh_byte_4; + lsm6ds3tr_c_sensorhub5_reg_t sh_byte_5; + lsm6ds3tr_c_sensorhub6_reg_t sh_byte_6; + lsm6ds3tr_c_sensorhub7_reg_t sh_byte_7; + lsm6ds3tr_c_sensorhub8_reg_t sh_byte_8; + lsm6ds3tr_c_sensorhub9_reg_t sh_byte_9; + lsm6ds3tr_c_sensorhub10_reg_t sh_byte_10; + lsm6ds3tr_c_sensorhub11_reg_t sh_byte_11; + lsm6ds3tr_c_sensorhub12_reg_t sh_byte_12; + lsm6ds3tr_c_sensorhub13_reg_t sh_byte_13; + lsm6ds3tr_c_sensorhub14_reg_t sh_byte_14; + lsm6ds3tr_c_sensorhub15_reg_t sh_byte_15; + lsm6ds3tr_c_sensorhub16_reg_t sh_byte_16; + lsm6ds3tr_c_sensorhub17_reg_t sh_byte_17; + lsm6ds3tr_c_sensorhub18_reg_t sh_byte_18; +} lsm6ds3tr_c_emb_sh_read_t; +int32_t lsm6ds3tr_c_sh_read_data_raw_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_emb_sh_read_t* val); + +int32_t lsm6ds3tr_c_sh_cmd_sens_sync_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_cmd_sens_sync_get(stmdev_ctx_t* ctx, uint8_t* val); + +int32_t lsm6ds3tr_c_sh_spi_sync_error_set(stmdev_ctx_t* ctx, uint8_t val); +int32_t lsm6ds3tr_c_sh_spi_sync_error_get(stmdev_ctx_t* ctx, uint8_t* val); + +typedef enum { + LSM6DS3TR_C_SLV_0 = 0, + LSM6DS3TR_C_SLV_0_1 = 1, + LSM6DS3TR_C_SLV_0_1_2 = 2, + LSM6DS3TR_C_SLV_0_1_2_3 = 3, + LSM6DS3TR_C_SLV_EN_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_aux_sens_on_t; +int32_t lsm6ds3tr_c_sh_num_of_dev_connected_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_aux_sens_on_t val); +int32_t lsm6ds3tr_c_sh_num_of_dev_connected_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_aux_sens_on_t* val); + +typedef struct { + uint8_t slv0_add; + uint8_t slv0_subadd; + uint8_t slv0_data; +} lsm6ds3tr_c_sh_cfg_write_t; +int32_t lsm6ds3tr_c_sh_cfg_write(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_write_t* val); + +typedef struct { + uint8_t slv_add; + uint8_t slv_subadd; + uint8_t slv_len; +} lsm6ds3tr_c_sh_cfg_read_t; +int32_t lsm6ds3tr_c_sh_slv0_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val); +int32_t lsm6ds3tr_c_sh_slv1_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val); +int32_t lsm6ds3tr_c_sh_slv2_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val); +int32_t lsm6ds3tr_c_sh_slv3_cfg_read(stmdev_ctx_t* ctx, lsm6ds3tr_c_sh_cfg_read_t* val); + +typedef enum { + LSM6DS3TR_C_SL0_NO_DEC = 0, + LSM6DS3TR_C_SL0_DEC_2 = 1, + LSM6DS3TR_C_SL0_DEC_4 = 2, + LSM6DS3TR_C_SL0_DEC_8 = 3, + LSM6DS3TR_C_SL0_DEC_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_slave0_rate_t; +int32_t lsm6ds3tr_c_sh_slave_0_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave0_rate_t val); +int32_t lsm6ds3tr_c_sh_slave_0_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave0_rate_t* val); + +typedef enum { + LSM6DS3TR_C_EACH_SH_CYCLE = 0, + LSM6DS3TR_C_ONLY_FIRST_CYCLE = 1, + LSM6DS3TR_C_SH_WR_MODE_ND = 2, /* ERROR CODE */ +} lsm6ds3tr_c_write_once_t; +int32_t lsm6ds3tr_c_sh_write_mode_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_write_once_t val); +int32_t lsm6ds3tr_c_sh_write_mode_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_write_once_t* val); + +typedef enum { + LSM6DS3TR_C_SL1_NO_DEC = 0, + LSM6DS3TR_C_SL1_DEC_2 = 1, + LSM6DS3TR_C_SL1_DEC_4 = 2, + LSM6DS3TR_C_SL1_DEC_8 = 3, + LSM6DS3TR_C_SL1_DEC_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_slave1_rate_t; +int32_t lsm6ds3tr_c_sh_slave_1_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave1_rate_t val); +int32_t lsm6ds3tr_c_sh_slave_1_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave1_rate_t* val); + +typedef enum { + LSM6DS3TR_C_SL2_NO_DEC = 0, + LSM6DS3TR_C_SL2_DEC_2 = 1, + LSM6DS3TR_C_SL2_DEC_4 = 2, + LSM6DS3TR_C_SL2_DEC_8 = 3, + LSM6DS3TR_C_SL2_DEC_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_slave2_rate_t; +int32_t lsm6ds3tr_c_sh_slave_2_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave2_rate_t val); +int32_t lsm6ds3tr_c_sh_slave_2_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave2_rate_t* val); + +typedef enum { + LSM6DS3TR_C_SL3_NO_DEC = 0, + LSM6DS3TR_C_SL3_DEC_2 = 1, + LSM6DS3TR_C_SL3_DEC_4 = 2, + LSM6DS3TR_C_SL3_DEC_8 = 3, + LSM6DS3TR_C_SL3_DEC_ND = 4, /* ERROR CODE */ +} lsm6ds3tr_c_slave3_rate_t; +int32_t lsm6ds3tr_c_sh_slave_3_dec_set(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave3_rate_t val); +int32_t lsm6ds3tr_c_sh_slave_3_dec_get(stmdev_ctx_t* ctx, lsm6ds3tr_c_slave3_rate_t* val); + +/** + * @} + * + */ + +#ifdef __cplusplus +} +#endif + +#endif /* LSM6DS3TR_C_DRIVER_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/main_loop.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/main_loop.cc new file mode 100644 index 000000000..2caffb717 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/main_loop.cc @@ -0,0 +1,189 @@ +#include "main_loop.h" + +#include +#include + +#include "imu/imu.h" +#include "orientation_tracker.h" +#include "calibration_data.h" + +#define TAG "tracker" + +static const float CURSOR_SPEED = 1024.0 / (M_PI / 4); +static const float STABILIZE_BIAS = 16.0; + +float g_yaw = 0; +float g_pitch = 0; +float g_dYaw = 0; +float g_dPitch = 0; +bool firstRead = true; +bool stabilize = true; +CalibrationData calibration; +cardboard::OrientationTracker tracker(10000000l); // 10 ms / 100 Hz +uint64_t ippms, ippms2; + +static inline float clamp(float val) +{ + while (val <= -M_PI) { + val += 2 * M_PI; + } + while (val >= M_PI) { + val -= 2 * M_PI; + } + return val; +} + +static inline float highpass(float oldVal, float newVal) +{ + if (!stabilize) { + return newVal; + } + float delta = clamp(oldVal - newVal); + float alpha = (float) std::max(0.0, 1 - std::pow(std::fabs(delta) * CURSOR_SPEED / STABILIZE_BIAS, 3.0)); + return newVal + alpha * delta; +} + +void sendCurrentState(MouseMoveCallback mouse_move, void *context) +{ + float dX = g_dYaw * CURSOR_SPEED; + float dY = g_dPitch * CURSOR_SPEED; + + // Scale the shift down to fit the protocol. + if (dX > 127) { + dY *= 127.0 / dX; + dX = 127; + } + if (dX < -127) { + dY *= -127.0 / dX; + dX = -127; + } + if (dY > 127) { + dX *= 127.0 / dY; + dY = 127; + } + if (dY < -127) { + dX *= -127.0 / dY; + dY = -127; + } + + const int8_t x = (int8_t)std::floor(dX + 0.5); + const int8_t y = (int8_t)std::floor(dY + 0.5); + + mouse_move(x, y, context); + + // Only subtract the part of the error that was already sent. + if (x != 0) { + g_dYaw -= x / CURSOR_SPEED; + } + if (y != 0) { + g_dPitch -= y / CURSOR_SPEED; + } +} + +void onOrientation(cardboard::Vector4& quaternion) +{ + float q1 = quaternion[0]; // X * sin(T/2) + float q2 = quaternion[1]; // Y * sin(T/2) + float q3 = quaternion[2]; // Z * sin(T/2) + float q0 = quaternion[3]; // cos(T/2) + + float yaw = std::atan2(2 * (q0 * q3 - q1 * q2), (1 - 2 * (q1 * q1 + q3 * q3))); + float pitch = std::asin(2 * (q0 * q1 + q2 * q3)); + // float roll = std::atan2(2 * (q0 * q2 - q1 * q3), (1 - 2 * (q1 * q1 + q2 * q2))); + + if (yaw == NAN || pitch == NAN) { + // NaN case, skip it + return; + } + + if (firstRead) { + g_yaw = yaw; + g_pitch = pitch; + firstRead = false; + } else { + const float newYaw = highpass(g_yaw, yaw); + const float newPitch = highpass(g_pitch, pitch); + + float dYaw = clamp(g_yaw - newYaw); + float dPitch = g_pitch - newPitch; + g_yaw = newYaw; + g_pitch = newPitch; + + // Accumulate the error locally. + g_dYaw += dYaw; + g_dPitch += dPitch; + } +} + +extern "C" { + +void calibration_begin() { + calibration.reset(); + FURI_LOG_I(TAG, "Calibrating"); +} + +bool calibration_step() { + if (calibration.isComplete()) + return true; + + double vec[6]; + if (imu_read(vec) & GYR_DATA_READY) { + cardboard::Vector3 data(vec[3], vec[4], vec[5]); + furi_delay_ms(9); // Artificially limit to ~100Hz + return calibration.add(data); + } + + return false; +} + +void calibration_end() { + CalibrationMedian store; + cardboard::Vector3 median = calibration.getMedian(); + store.x = median[0]; + store.y = median[1]; + store.z = median[2]; + CALIBRATION_DATA_SAVE(&store); +} + +void tracking_begin() { + CalibrationMedian store; + cardboard::Vector3 median = calibration.getMedian(); + if (CALIBRATION_DATA_LOAD(&store)) { + median[0] = store.x; + median[1] = store.y; + median[2] = store.z; + } + + ippms = furi_hal_cortex_instructions_per_microsecond(); + ippms2 = ippms / 2; + tracker.SetCalibration(median); + tracker.Resume(); +} + +void tracking_step(MouseMoveCallback mouse_move, void *context) { + double vec[6]; + int ret = imu_read(vec); + if (ret != 0) { + uint64_t t = (DWT->CYCCNT * 1000llu + ippms2) / ippms; + if (ret & ACC_DATA_READY) { + cardboard::AccelerometerData adata + = { .system_timestamp = t, .sensor_timestamp_ns = t, + .data = cardboard::Vector3(vec[0], vec[1], vec[2]) }; + tracker.OnAccelerometerData(adata); + } + if (ret & GYR_DATA_READY) { + cardboard::GyroscopeData gdata + = { .system_timestamp = t, .sensor_timestamp_ns = t, + .data = cardboard::Vector3(vec[3], vec[4], vec[5]) }; + cardboard::Vector4 pose = tracker.OnGyroscopeData(gdata); + onOrientation(pose); + sendCurrentState(mouse_move, context); + } + } +} + +void tracking_end() { + tracker.Pause(); +} + +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/main_loop.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/main_loop.h new file mode 100644 index 000000000..cd592161f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/main_loop.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef bool (*MouseMoveCallback)(int8_t x, int8_t y, void* context); + +void calibration_begin(); +bool calibration_step(); +void calibration_end(); + +void tracking_begin(); +void tracking_step(MouseMoveCallback mouse_move, void* context); +void tracking_end(); + +#ifdef __cplusplus +} +#endif diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/orientation_tracker.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/orientation_tracker.cc new file mode 100644 index 000000000..ac20e9672 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/orientation_tracker.cc @@ -0,0 +1,95 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "orientation_tracker.h" + +#include "sensors/pose_prediction.h" +#include "util/logging.h" +#include "util/vector.h" +#include "util/vectorutils.h" + +namespace cardboard { + +OrientationTracker::OrientationTracker(const long sampling_period_ns) + : sampling_period_ns_(sampling_period_ns) + , calibration_(Vector3::Zero()) + , is_tracking_(false) + , sensor_fusion_(new SensorFusionEkf()) + , latest_gyroscope_data_({ 0, 0, Vector3::Zero() }) +{ + sensor_fusion_->SetBiasEstimationEnabled(/*kGyroBiasEstimationEnabled*/ true); +} + +void OrientationTracker::SetCalibration(const Vector3& calibration) { + calibration_ = calibration; +} + +void OrientationTracker::Pause() +{ + if (!is_tracking_) { + return; + } + + // Create a gyro event with zero velocity. This effectively stops the prediction. + GyroscopeData event = latest_gyroscope_data_; + event.data = Vector3::Zero(); + + OnGyroscopeData(event); + + is_tracking_ = false; +} + +void OrientationTracker::Resume() { is_tracking_ = true; } + +Vector4 OrientationTracker::GetPose(int64_t timestamp_ns) const +{ + Rotation predicted_rotation; + const PoseState pose_state = sensor_fusion_->GetLatestPoseState(); + if (sensor_fusion_->IsFullyInitialized()) { + predicted_rotation = pose_state.sensor_from_start_rotation; + } else { + CARDBOARD_LOGI("Tracker not fully initialized yet. Using pose prediction only."); + predicted_rotation = pose_prediction::PredictPose(timestamp_ns, pose_state); + } + + return (-predicted_rotation).GetQuaternion(); +} + +void OrientationTracker::OnAccelerometerData(const AccelerometerData& event) +{ + if (!is_tracking_) { + return; + } + sensor_fusion_->ProcessAccelerometerSample(event); +} + +Vector4 OrientationTracker::OnGyroscopeData(const GyroscopeData& event) +{ + if (!is_tracking_) { + return Vector4(); + } + + const GyroscopeData data = { .system_timestamp = event.system_timestamp, + .sensor_timestamp_ns = event.sensor_timestamp_ns, + .data = event.data - calibration_ }; + + latest_gyroscope_data_ = data; + + sensor_fusion_->ProcessGyroscopeSample(data); + + return OrientationTracker::GetPose(data.sensor_timestamp_ns + sampling_period_ns_); +} + +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/orientation_tracker.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/orientation_tracker.h new file mode 100644 index 000000000..fb49c9859 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/orientation_tracker.h @@ -0,0 +1,68 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include +#include // NOLINT + +#include "sensors/accelerometer_data.h" +#include "sensors/gyroscope_data.h" +#include "sensors/sensor_fusion_ekf.h" +#include "util/rotation.h" + +namespace cardboard { + +// OrientationTracker encapsulates pose tracking by connecting sensors +// to SensorFusion. +// This pose tracker reports poses in display space. +class OrientationTracker { +public: + OrientationTracker(const long sampling_period_ns); + + void SetCalibration(const Vector3& calibration); + + // Pauses tracking and sensors. + void Pause(); + + // Resumes tracking ans sensors. + void Resume(); + + // Gets the predicted pose for a given timestamp. + Vector4 GetPose(int64_t timestamp_ns) const; + + // Function called when receiving AccelerometerData. + // + // @param event sensor event. + void OnAccelerometerData(const AccelerometerData& event); + + // Function called when receiving GyroscopeData. + // + // @param event sensor event. + Vector4 OnGyroscopeData(const GyroscopeData& event); + +private: + long sampling_period_ns_; + Vector3 calibration_; + + std::atomic is_tracking_; + // Sensor Fusion object that stores the internal state of the filter. + std::unique_ptr sensor_fusion_; + // Latest gyroscope data. + GyroscopeData latest_gyroscope_data_; +}; + +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/accelerometer_data.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/accelerometer_data.h new file mode 100644 index 000000000..bdf3289af --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/accelerometer_data.h @@ -0,0 +1,38 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_ACCELEROMETER_DATA_H_ +#define CARDBOARD_SDK_SENSORS_ACCELEROMETER_DATA_H_ + +#include "../util/vector.h" + +namespace cardboard { + +struct AccelerometerData { + // System wall time. + uint64_t system_timestamp; + + // Sensor clock time in nanoseconds. + uint64_t sensor_timestamp_ns; + + // Acceleration force along the x,y,z axes in m/s^2. This follows android + // specification + // (https://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-coords). + Vector3 data; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_ACCELEROMETER_DATA_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/gyroscope_bias_estimator.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/gyroscope_bias_estimator.cc new file mode 100644 index 000000000..96f2f7346 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/gyroscope_bias_estimator.cc @@ -0,0 +1,313 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "gyroscope_bias_estimator.h" + +#include +#include // NOLINT + +#include "../util/rotation.h" +#include "../util/vector.h" + +namespace { + +// Cutoff frequencies in Hertz applied to our various signals, and their +// corresponding filters. +const float kAccelerometerLowPassCutOffFrequencyHz = 1.0f; +const float kRotationVelocityBasedAccelerometerLowPassCutOffFrequencyHz = 0.15f; +const float kGyroscopeLowPassCutOffFrequencyHz = 1.0f; +const float kGyroscopeBiasLowPassCutOffFrequencyHz = 0.15f; + +// Note that MEMS IMU are not that precise. +const double kEpsilon = 1.0e-8; + +// Size of the filtering window for the mean and median filter. The larger the +// windows the larger the filter delay. +const int kFilterWindowSize = 5; + +// Threshold used to compare rotation computed from the accelerometer and the +// gyroscope bias. +const double kRatioBetweenGyroBiasAndAccel = 1.5; + +// The minimum sum of weights we need to acquire before returning a bias +// estimation. +const float kMinSumOfWeightsGyroBiasThreshold = 25.0f; + +// Amount of change in m/s^3 we allow on the smoothed accelerometer values to +// consider the phone static. +const double kAccelerometerDeltaStaticThreshold = 0.5; + +// Amount of change in radians/s^2 we allow on the smoothed gyroscope values to +// consider the phone static. +const double kGyroscopeDeltaStaticThreshold = 0.03; + +// If the gyroscope value is above this threshold, don't update the gyroscope +// bias estimation. This threshold is applied to the magnitude of gyroscope +// vectors in radians/s. +const float kGyroscopeForBiasThreshold = 0.30f; + +// Used to monitor if accelerometer and gyroscope have been static for a few +// frames. +const int kStaticFrameDetectionThreshold = 50; + +// Minimum time step between sensor updates. +const double kMinTimestep = 1; // std::chrono::nanoseconds(1); +} // namespace + +namespace cardboard { + +// A helper class to keep track of whether some signal can be considered static +// over specified number of frames. +class GyroscopeBiasEstimator::IsStaticCounter { +public: + // Initializes a counter with the number of consecutive frames we require + // the signal to be static before IsRecentlyStatic returns true. + // + // @param min_static_frames_threshold number of consecutive frames we + // require the signal to be static before IsRecentlyStatic returns true. + explicit IsStaticCounter(int min_static_frames_threshold) + : min_static_frames_threshold_(min_static_frames_threshold) + , consecutive_static_frames_(0) + { + } + + // Specifies whether the current frame is considered static. + // + // @param is_static static flag for current frame. + void AppendFrame(bool is_static) + { + if (is_static) { + ++consecutive_static_frames_; + } else { + consecutive_static_frames_ = 0; + } + } + + // Returns if static movement is assumed. + bool IsRecentlyStatic() const + { + return consecutive_static_frames_ >= min_static_frames_threshold_; + } + // Resets counter. + void Reset() { consecutive_static_frames_ = 0; } + +private: + const int min_static_frames_threshold_; + int consecutive_static_frames_; +}; + +GyroscopeBiasEstimator::GyroscopeBiasEstimator() + : accelerometer_lowpass_filter_(kAccelerometerLowPassCutOffFrequencyHz) + , simulated_gyroscope_from_accelerometer_lowpass_filter_( + kRotationVelocityBasedAccelerometerLowPassCutOffFrequencyHz) + , gyroscope_lowpass_filter_(kGyroscopeLowPassCutOffFrequencyHz) + , gyroscope_bias_lowpass_filter_(kGyroscopeBiasLowPassCutOffFrequencyHz) + , accelerometer_static_counter_(new IsStaticCounter(kStaticFrameDetectionThreshold)) + , gyroscope_static_counter_(new IsStaticCounter(kStaticFrameDetectionThreshold)) + , current_accumulated_weights_gyroscope_bias_(0.f) + , mean_filter_(kFilterWindowSize) + , median_filter_(kFilterWindowSize) + , last_mean_filtered_accelerometer_value_({ 0, 0, 0 }) +{ + Reset(); +} + +GyroscopeBiasEstimator::~GyroscopeBiasEstimator() { } + +void GyroscopeBiasEstimator::Reset() +{ + accelerometer_lowpass_filter_.Reset(); + gyroscope_lowpass_filter_.Reset(); + gyroscope_bias_lowpass_filter_.Reset(); + accelerometer_static_counter_->Reset(); + gyroscope_static_counter_->Reset(); +} + +void GyroscopeBiasEstimator::ProcessGyroscope( + const Vector3& gyroscope_sample, uint64_t timestamp_ns) +{ + // Update gyroscope and gyroscope delta low-pass filters. + gyroscope_lowpass_filter_.AddSample(gyroscope_sample, timestamp_ns); + + const auto smoothed_gyroscope_delta + = gyroscope_sample - gyroscope_lowpass_filter_.GetFilteredData(); + + gyroscope_static_counter_->AppendFrame( + Length(smoothed_gyroscope_delta) < kGyroscopeDeltaStaticThreshold); + + // Only update the bias if the gyroscope and accelerometer signals have been + // relatively static recently. + if (gyroscope_static_counter_->IsRecentlyStatic() + && accelerometer_static_counter_->IsRecentlyStatic()) { + // Reset static counter when updating the bias fails. + if (!UpdateGyroscopeBias(gyroscope_sample, timestamp_ns)) { + // Bias update fails because of large motion, thus reset the static + // counter. + gyroscope_static_counter_->AppendFrame(false); + } + } else { + // Reset weights, if not static. + current_accumulated_weights_gyroscope_bias_ = 0; + } +} + +void GyroscopeBiasEstimator::ProcessAccelerometer( + const Vector3& accelerometer_sample, uint64_t timestamp_ns) +{ + // Get current state of the filter. + const uint64_t previous_accel_timestamp_ns + = accelerometer_lowpass_filter_.GetMostRecentTimestampNs(); + const bool is_low_pass_filter_init = accelerometer_lowpass_filter_.IsInitialized(); + + // Update accel and accel delta low-pass filters. + accelerometer_lowpass_filter_.AddSample(accelerometer_sample, timestamp_ns); + + const auto smoothed_accelerometer_delta + = accelerometer_sample - accelerometer_lowpass_filter_.GetFilteredData(); + + accelerometer_static_counter_->AppendFrame( + Length(smoothed_accelerometer_delta) < kAccelerometerDeltaStaticThreshold); + + // Rotation from accel cannot be differentiated with only one sample. + if (!is_low_pass_filter_init) { + simulated_gyroscope_from_accelerometer_lowpass_filter_.AddSample({ 0, 0, 0 }, timestamp_ns); + return; + } + + // No need to update the simulated gyroscope at this point because the motion + // is too large. + if (!accelerometer_static_counter_->IsRecentlyStatic()) { + return; + } + + median_filter_.AddSample(accelerometer_lowpass_filter_.GetFilteredData()); + + // This processing can only be started if the buffer is fully initialized. + if (!median_filter_.IsValid()) { + mean_filter_.AddSample(accelerometer_lowpass_filter_.GetFilteredData()); + + // Update the last filtered accelerometer value. + last_mean_filtered_accelerometer_value_ = accelerometer_lowpass_filter_.GetFilteredData(); + return; + } + + mean_filter_.AddSample(median_filter_.GetFilteredData()); + + // Compute a mock gyroscope value from accelerometer. + const int64_t diff = timestamp_ns - previous_accel_timestamp_ns; + const double timestep = static_cast(diff); + + simulated_gyroscope_from_accelerometer_lowpass_filter_.AddSample( + ComputeAngularVelocityFromLatestAccelerometer(timestep), timestamp_ns); + last_mean_filtered_accelerometer_value_ = mean_filter_.GetFilteredData(); +} + +Vector3 GyroscopeBiasEstimator::ComputeAngularVelocityFromLatestAccelerometer(double timestep) const +{ + if (timestep < kMinTimestep) { + return { 0, 0, 0 }; + } + + const auto mean_of_median = mean_filter_.GetFilteredData(); + + // Compute an incremental rotation between the last state and the current + // state. + // + // Note that we switch to double precision here because of precision problem + // with small rotation. + const auto incremental_rotation = Rotation::RotateInto( + Vector3(last_mean_filtered_accelerometer_value_[0], + last_mean_filtered_accelerometer_value_[1], last_mean_filtered_accelerometer_value_[2]), + Vector3(mean_of_median[0], mean_of_median[1], mean_of_median[2])); + + // We use axis angle here because this is how gyroscope values are stored. + Vector3 incremental_rotation_axis; + double incremental_rotation_angle; + incremental_rotation.GetAxisAndAngle(&incremental_rotation_axis, &incremental_rotation_angle); + + incremental_rotation_axis *= incremental_rotation_angle / timestep; + + return { static_cast(incremental_rotation_axis[0]), + static_cast(incremental_rotation_axis[1]), + static_cast(incremental_rotation_axis[2]) }; +} + +bool GyroscopeBiasEstimator::UpdateGyroscopeBias( + const Vector3& gyroscope_sample, uint64_t timestamp_ns) +{ + // Gyroscope values that are too big are potentially dangerous as they could + // originate from slow and steady head rotations. + // + // Therefore we compute an update weight which: + // * favors gyroscope values that are closer to 0 + // * is set to zero if gyroscope values are greater than a threshold. + // + // This way, the gyroscope bias estimation converges faster if the phone is + // flat on a table, as opposed to held up somewhat stationary in the user's + // hands. + + // If magnitude is too big, don't update the filter at all so that we don't + // artificially increase the number of samples accumulated by the filter. + const float gyroscope_sample_norm2 = Length(gyroscope_sample); + if (gyroscope_sample_norm2 >= kGyroscopeForBiasThreshold) { + return false; + } + + float update_weight + = std::max(0.0f, 1 - gyroscope_sample_norm2 / kGyroscopeForBiasThreshold); + update_weight *= update_weight; + gyroscope_bias_lowpass_filter_.AddWeightedSample( + gyroscope_lowpass_filter_.GetFilteredData(), timestamp_ns, update_weight); + + // This counter is only partially valid as the low pass filter drops large + // samples. + current_accumulated_weights_gyroscope_bias_ += update_weight; + + return true; +} + +Vector3 GyroscopeBiasEstimator::GetGyroscopeBias() const +{ + return gyroscope_bias_lowpass_filter_.GetFilteredData(); +} + +bool GyroscopeBiasEstimator::IsCurrentEstimateValid() const +{ + // Remove any bias component along the gravity because they cannot be + // evaluated from accelerometer. + const auto current_gravity_dir = Normalized(last_mean_filtered_accelerometer_value_); + const auto gyro_bias_lowpass = gyroscope_bias_lowpass_filter_.GetFilteredData(); + + const auto off_gravity_gyro_bias + = gyro_bias_lowpass - current_gravity_dir * Dot(gyro_bias_lowpass, current_gravity_dir); + + // Checks that the current bias estimate is not correlated with the + // rotation computed from accelerometer. + const auto gyro_from_accel + = simulated_gyroscope_from_accelerometer_lowpass_filter_.GetFilteredData(); + const bool isGyroscopeBiasCorrelatedWithSimulatedGyro + = (Length(gyro_from_accel) * kRatioBetweenGyroBiasAndAccel + > (Length(off_gravity_gyro_bias) + kEpsilon)); + const bool hasEnoughSamples + = current_accumulated_weights_gyroscope_bias_ > kMinSumOfWeightsGyroBiasThreshold; + const bool areCountersStatic = gyroscope_static_counter_->IsRecentlyStatic() + && accelerometer_static_counter_->IsRecentlyStatic(); + + const bool isStatic + = hasEnoughSamples && areCountersStatic && !isGyroscopeBiasCorrelatedWithSimulatedGyro; + return isStatic; +} + +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/gyroscope_bias_estimator.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/gyroscope_bias_estimator.h new file mode 100644 index 000000000..1a46f96be --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/gyroscope_bias_estimator.h @@ -0,0 +1,134 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_GYROSCOPE_BIAS_ESTIMATOR_H_ +#define CARDBOARD_SDK_SENSORS_GYROSCOPE_BIAS_ESTIMATOR_H_ + +#include // NOLINT +#include +#include +#include +#include + +#include "lowpass_filter.h" +#include "mean_filter.h" +#include "median_filter.h" +#include "../util/vector.h" + +namespace cardboard { + +// Class that attempts to estimate the gyroscope's bias. +// Its main idea is that it averages the gyroscope values when the phone is +// considered stationary. +// Usage: A client should call the ProcessGyroscope and ProcessAccelerometer +// methods for every accelerometer and gyroscope sensor sample. This class +// expects these calls to be frequent, i.e., at least at 10 Hz. The client can +// then call GetGyroBias to retrieve the current estimate of the gyroscope bias. +// For best results, the fastest available delay option should be used when +// registering to sensors. Note that this class is not thread-safe. +// +// The filtering applied to the accelerometer to estimate a rotation +// from it follows : +// Baptiste Delporte, Laurent Perroton, Thierry Grandpierre, Jacques Trichet. +// Accelerometer and Magnetometer Based Gyroscope Emulation on Smart Sensor +// for a Virtual Reality Application. Sensor and Transducers Journal, 2012. +// +// which is a combination of a IIR filter, a median and a mean filter. +class GyroscopeBiasEstimator { +public: + GyroscopeBiasEstimator(); + virtual ~GyroscopeBiasEstimator(); + + // Updates the estimator with a gyroscope event. + // + // @param gyroscope_sample the angular speed around the x, y, z axis in + // radians/sec. + // @param timestamp_ns the nanosecond at which the event occurred. Only + // guaranteed to be comparable with timestamps from other PocessGyroscope + // invocations. + virtual void ProcessGyroscope(const Vector3& gyroscope_sample, uint64_t timestamp_ns); + + // Processes accelerometer samples to estimate if device is + // stable or not. + // + // First we filter the accelerometer. This is done with 3 filters. + // - A IIR low-pass filter + // - A median filter + // - A mean filter. + // Then a rotation is computed between consecutive filtered accelerometer + // samples. + // Finally this is converted to a velocity to emulate a gyroscope. + // + // @param accelerometer_sample the acceleration (including gravity) on the x, + // y, z axis in meters/s^2. + // @param timestamp_ns the nanosecond at which the event occurred. Only + // guaranteed to be comparable with timestamps from other + // ProcessAccelerometer invocations. + virtual void ProcessAccelerometer(const Vector3& accelerometer_sample, uint64_t timestamp_ns); + + // Returns the estimated gyroscope bias. + // + // @return Estimated gyroscope bias. A vector with zeros is returned if no + // estimate has been computed. + virtual Vector3 GetGyroscopeBias() const; + + // Resets the estimator state. + void Reset(); + + // Returns true if the current estimate returned by GetGyroscopeBias is + // correct. The device (measured using the sensors) has to be static for this + // function to return true. + virtual bool IsCurrentEstimateValid() const; + +private: + // A helper class to keep track of whether some signal can be considered + // static over specified number of frames. + class IsStaticCounter; + + // Updates gyroscope bias estimation. + // + // @return false if the current sample is too large. + bool UpdateGyroscopeBias(const Vector3& gyroscope_sample, uint64_t timestamp_ns); + + // Returns device angular velocity (rad/s) from the latest accelerometer data. + // + // @param timestep in seconds between the last two samples. + // @return rotation velocity from latest accelerometer. This can be + // interpreted as an gyroscope. + Vector3 ComputeAngularVelocityFromLatestAccelerometer(double timestep) const; + + LowpassFilter accelerometer_lowpass_filter_; + LowpassFilter simulated_gyroscope_from_accelerometer_lowpass_filter_; + LowpassFilter gyroscope_lowpass_filter_; + LowpassFilter gyroscope_bias_lowpass_filter_; + + std::unique_ptr accelerometer_static_counter_; + std::unique_ptr gyroscope_static_counter_; + + // Sum of the weight of sample used for gyroscope filtering. + float current_accumulated_weights_gyroscope_bias_; + + // Set of filters for accelerometer data to estimate a rotation + // based only on accelerometer. + MeanFilter mean_filter_; + MedianFilter median_filter_; + + // Last computed filter accelerometer value used for finite differences. + Vector3 last_mean_filtered_accelerometer_value_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_GYROSCOPE_BIAS_ESTIMATOR_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/gyroscope_data.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/gyroscope_data.h new file mode 100644 index 000000000..085e85209 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/gyroscope_data.h @@ -0,0 +1,38 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_GYROSCOPE_DATA_H_ +#define CARDBOARD_SDK_SENSORS_GYROSCOPE_DATA_H_ + +#include "../util/vector.h" + +namespace cardboard { + +struct GyroscopeData { + // System wall time. + uint64_t system_timestamp; + + // Sensor clock time in nanoseconds. + uint64_t sensor_timestamp_ns; + + // Rate of rotation around the x,y,z axes in rad/s. This follows android + // specification + // (https://developer.android.com/guide/topics/sensors/sensors_overview.html#sensors-coords). + Vector3 data; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_GYROSCOPE_DATA_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/lowpass_filter.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/lowpass_filter.cc new file mode 100644 index 000000000..efadfbf4e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/lowpass_filter.cc @@ -0,0 +1,84 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "lowpass_filter.h" + +#include + +namespace { + +const double kSecondsFromNanoseconds = 1.0e-9; + +// Minimum time step between sensor updates. This corresponds to 1000 Hz. +const double kMinTimestepS = 0.001f; + +// Maximum time step between sensor updates. This corresponds to 1 Hz. +const double kMaxTimestepS = 1.00f; + +} // namespace + +namespace cardboard { + +LowpassFilter::LowpassFilter(double cutoff_freq_hz) + : cutoff_time_constant_(1 / (2 * (double)M_PI * cutoff_freq_hz)) + , initialized_(false) +{ + Reset(); +} + +void LowpassFilter::AddSample(const Vector3& sample, uint64_t timestamp_ns) +{ + AddWeightedSample(sample, timestamp_ns, 1.0); +} + +void LowpassFilter::AddWeightedSample(const Vector3& sample, uint64_t timestamp_ns, double weight) +{ + if (!initialized_) { + // Initialize filter state + filtered_data_ = { sample[0], sample[1], sample[2] }; + timestamp_most_recent_update_ns_ = timestamp_ns; + initialized_ = true; + return; + } + + if (timestamp_ns < timestamp_most_recent_update_ns_) { + timestamp_most_recent_update_ns_ = timestamp_ns; + return; + } + + const double delta_s = static_cast(timestamp_ns - timestamp_most_recent_update_ns_) + * kSecondsFromNanoseconds; + if (delta_s <= kMinTimestepS || delta_s > kMaxTimestepS) { + timestamp_most_recent_update_ns_ = timestamp_ns; + return; + } + + const double weighted_delta_secs = weight * delta_s; + + const double alpha = weighted_delta_secs / (cutoff_time_constant_ + weighted_delta_secs); + + for (int i = 0; i < 3; ++i) { + filtered_data_[i] = (1 - alpha) * filtered_data_[i] + alpha * sample[i]; + } + timestamp_most_recent_update_ns_ = timestamp_ns; +} + +void LowpassFilter::Reset() +{ + initialized_ = false; + filtered_data_ = { 0, 0, 0 }; +} + +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/lowpass_filter.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/lowpass_filter.h new file mode 100644 index 000000000..c4994c425 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/lowpass_filter.h @@ -0,0 +1,81 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_LOWPASS_FILTER_H_ +#define CARDBOARD_SDK_SENSORS_LOWPASS_FILTER_H_ + +#include +#include + +#include "../util/vector.h" + +namespace cardboard { + +// Implements an IIR, first order, low pass filter over vectors of the given +// dimension = 3. +// See http://en.wikipedia.org/wiki/Low-pass_filter +class LowpassFilter { +public: + // Initializes a filter with the given cutoff frequency in Hz. + explicit LowpassFilter(double cutoff_freq_hz); + + // Updates the filter with the given sample. Note that samples with + // non-monotonic timestamps and successive samples with a time steps below 1 + // ms or above 1 s are ignored. + // + // @param sample current sample data. + // @param timestamp_ns timestamp associated to this sample in nanoseconds. + void AddSample(const Vector3& sample, uint64_t timestamp_ns); + + // Updates the filter with the given weighted sample. + // + // @param sample current sample data. + // @param timestamp_ns timestamp associated to this sample in nanoseconds. + // @param weight typically a [0, 1] weight factor used when applying a new + // sample. A weight of 1 corresponds to calling AddSample. A weight of 0 + // makes the update no-op. The first initial sample is not affected by + // this. + void AddWeightedSample(const Vector3& sample, uint64_t timestamp_ns, double weight); + + // Returns the filtered value. A vector with zeros is returned if no samples + // have been added. + Vector3 GetFilteredData() const { + return filtered_data_; + } + + // Returns the most recent update timestamp in ns. + uint64_t GetMostRecentTimestampNs() const { + return timestamp_most_recent_update_ns_; + } + + // Returns true when the filter is initialized. + bool IsInitialized() const { + return initialized_; + } + + // Resets filter state. + void Reset(); + +private: + const double cutoff_time_constant_; + uint64_t timestamp_most_recent_update_ns_; + bool initialized_; + + Vector3 filtered_data_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_LOWPASS_FILTER_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/mean_filter.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/mean_filter.cc new file mode 100644 index 000000000..02fb8034c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/mean_filter.cc @@ -0,0 +1,46 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mean_filter.h" + +namespace cardboard { + +MeanFilter::MeanFilter(size_t filter_size) + : filter_size_(filter_size) +{ +} + +void MeanFilter::AddSample(const Vector3& sample) +{ + buffer_.push_back(sample); + if (buffer_.size() > filter_size_) { + buffer_.pop_front(); + } +} + +bool MeanFilter::IsValid() const { return buffer_.size() == filter_size_; } + +Vector3 MeanFilter::GetFilteredData() const +{ + // Compute mean of the samples stored in buffer_. + Vector3 mean = Vector3::Zero(); + for (auto sample : buffer_) { + mean += sample; + } + + return mean / static_cast(filter_size_); +} + +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/mean_filter.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/mean_filter.h new file mode 100644 index 000000000..6b4956fef --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/mean_filter.h @@ -0,0 +1,48 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_MEAN_FILTER_H_ +#define CARDBOARD_SDK_SENSORS_MEAN_FILTER_H_ + +#include + +#include "../util/vector.h" + +namespace cardboard { + +// Fixed window FIFO mean filter for vectors of the given dimension. +class MeanFilter { +public: + // Create a mean filter of size filter_size. + // @param filter_size size of the internal filter. + explicit MeanFilter(size_t filter_size); + + // Add sample to buffer_ if buffer_ is full it drop the oldest sample. + void AddSample(const Vector3& sample); + + // Returns true if buffer has filter_size_ sample, false otherwise. + bool IsValid() const; + + // Returns the mean of values stored in the internal buffer. + Vector3 GetFilteredData() const; + +private: + const size_t filter_size_; + std::deque buffer_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_MEAN_FILTER_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/median_filter.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/median_filter.cc new file mode 100644 index 000000000..d27c19f47 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/median_filter.cc @@ -0,0 +1,69 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "median_filter.h" + +#include +#include + +#include "../util/vector.h" +#include "../util/vectorutils.h" + +namespace cardboard { + +MedianFilter::MedianFilter(size_t filter_size) + : filter_size_(filter_size) +{ +} + +void MedianFilter::AddSample(const Vector3& sample) +{ + buffer_.push_back(sample); + norms_.push_back(Length(sample)); + if (buffer_.size() > filter_size_) { + buffer_.pop_front(); + norms_.pop_front(); + } +} + +bool MedianFilter::IsValid() const { return buffer_.size() == filter_size_; } + +Vector3 MedianFilter::GetFilteredData() const +{ + std::vector norms(norms_.begin(), norms_.end()); + + // Get median of value of the norms. + std::nth_element(norms.begin(), norms.begin() + filter_size_ / 2, norms.end()); + const float median_norm = norms[filter_size_ / 2]; + + // Get median value based on their norm. + auto median_it = buffer_.begin(); + for (const auto norm : norms_) { + if (norm == median_norm) { + break; + } + ++median_it; + } + + return *median_it; +} + +void MedianFilter::Reset() +{ + buffer_.clear(); + norms_.clear(); +} + +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/median_filter.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/median_filter.h new file mode 100644 index 000000000..9a8e7cfc7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/median_filter.h @@ -0,0 +1,53 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_MEDIAN_FILTER_H_ +#define CARDBOARD_SDK_SENSORS_MEDIAN_FILTER_H_ + +#include + +#include "../util/vector.h" + +namespace cardboard { + +// Fixed window FIFO median filter for vectors of the given dimension = 3. +class MedianFilter { +public: + // Creates a median filter of size filter_size. + // @param filter_size size of the internal filter. + explicit MedianFilter(size_t filter_size); + + // Adds sample to buffer_ if buffer_ is full it drops the oldest sample. + void AddSample(const Vector3& sample); + + // Returns true if buffer has filter_size_ sample, false otherwise. + bool IsValid() const; + + // Returns the median of values store in the internal buffer. + Vector3 GetFilteredData() const; + + // Resets the filter, removing all samples that have been added. + void Reset(); + +private: + const size_t filter_size_; + std::deque buffer_; + // Contains norms of the elements stored in buffer_. + std::deque norms_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_MEDIAN_FILTER_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/pose_prediction.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/pose_prediction.cc new file mode 100644 index 000000000..baaf844dd --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/pose_prediction.cc @@ -0,0 +1,71 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "pose_prediction.h" + +#include // NOLINT + +#include "../util/logging.h" +#include "../util/vectorutils.h" + +namespace cardboard { + +namespace { + const double kEpsilon = 1.0e-15; +} // namespace + +namespace pose_prediction { + + Rotation GetRotationFromGyroscope(const Vector3& gyroscope_value, double timestep_s) + { + const double velocity = Length(gyroscope_value); + + // When there is no rotation data return an identity rotation. + if (velocity < kEpsilon) { + CARDBOARD_LOGI("PosePrediction::GetRotationFromGyroscope: Velocity really small, " + "returning identity rotation."); + return Rotation::Identity(); + } + // Since the gyroscope_value is a start from sensor transformation we need to + // invert it to have a sensor from start transformation, hence the minus sign. + // For more info: + // http://developer.android.com/guide/topics/sensors/sensors_motion.html#sensors-motion-gyro + return Rotation::FromAxisAndAngle(gyroscope_value / velocity, -timestep_s * velocity); + } + + Rotation PredictPose(int64_t requested_pose_timestamp, const PoseState& current_state) + { + // Subtracting unsigned numbers is bad when the result is negative. + const int64_t diff = requested_pose_timestamp - current_state.timestamp; + const double timestep_s = diff * 1.0e-9; + + const Rotation update = GetRotationFromGyroscope( + current_state.sensor_from_start_rotation_velocity, timestep_s); + return update * current_state.sensor_from_start_rotation; + } + + Rotation PredictPoseInv(int64_t requested_pose_timestamp, const PoseState& current_state) + { + // Subtracting unsigned numbers is bad when the result is negative. + const int64_t diff = requested_pose_timestamp - current_state.timestamp; + const double timestep_s = diff * 1.0e-9; + + const Rotation update = GetRotationFromGyroscope( + current_state.sensor_from_start_rotation_velocity, timestep_s); + return current_state.sensor_from_start_rotation * (-update); + } + +} // namespace pose_prediction +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/pose_prediction.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/pose_prediction.h new file mode 100644 index 000000000..9ab311b33 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/pose_prediction.h @@ -0,0 +1,55 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_POSE_PREDICTION_H_ +#define CARDBOARD_SDK_SENSORS_POSE_PREDICTION_H_ + +#include + +#include "pose_state.h" +#include "../util/rotation.h" + +namespace cardboard { +namespace pose_prediction { + +// Returns a rotation matrix based on the integration of the gyroscope_value +// over the timestep_s in seconds. +// TODO(pfg): Document the space better here. +// +// @param gyroscope_value gyroscope sensor values. +// @param timestep_s integration period in seconds. +// @return Integration of the gyroscope value the rotation is from Start to +// Sensor Space. +Rotation GetRotationFromGyroscope(const Vector3& gyroscope_value, double timestep_s); + +// Gets a predicted pose for a given time in the future (e.g. rendering time) +// based on a linear prediction model. This uses the system current state +// (position, velocity, etc) from the past to extrapolate a position in the +// future. +// +// @param requested_pose_timestamp time at which you want the pose. +// @param current_state current state that stores the pose and linear model at a +// given time prior to requested_pose_timestamp_ns. +// @return pose from Start to Sensor Space. +Rotation PredictPose(int64_t requested_pose_timestamp, const PoseState& current_state); + +// Equivalent to PredictPose, but for use with poses relative to Start Space +// rather than sensor space. +Rotation PredictPoseInv(int64_t requested_pose_timestamp, const PoseState& current_state); + +} // namespace pose_prediction +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_POSE_PREDICTION_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/pose_state.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/pose_state.h new file mode 100644 index 000000000..f7801c9f3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/pose_state.h @@ -0,0 +1,56 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_POSE_STATE_H_ +#define CARDBOARD_SDK_SENSORS_POSE_STATE_H_ + +#include "../util/rotation.h" +#include "../util/vector.h" + +namespace cardboard { + +enum { + kPoseStateFlagInvalid = 1U << 0, + kPoseStateFlagInitializing = 1U << 1, + kPoseStateFlagHas6DoF = 1U << 2, +}; + +// Stores a head pose pose plus derivatives. This can be used for prediction. +struct PoseState { + // System wall time. + int64_t timestamp; + + // Rotation from Sensor Space to Start Space. + Rotation sensor_from_start_rotation; + + // First derivative of the rotation. + Vector3 sensor_from_start_rotation_velocity; + + // Current gyroscope bias in rad/s. + Vector3 bias; + + // The position of the headset. + Vector3 position = Vector3(0, 0, 0); + + // In the same coordinate frame as the position. + Vector3 velocity = Vector3(0, 0, 0); + + // Flags indicating the status of the pose. + uint64_t flags = 0U; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_POSE_STATE_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/sensor_fusion_ekf.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/sensor_fusion_ekf.cc new file mode 100644 index 000000000..575dde6f0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/sensor_fusion_ekf.cc @@ -0,0 +1,333 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "sensor_fusion_ekf.h" + +#include +#include + +#include "accelerometer_data.h" +#include "gyroscope_data.h" +#include "pose_prediction.h" +#include "../util/matrixutils.h" + +namespace cardboard { + +namespace { + + const double kFiniteDifferencingEpsilon = 1.0e-7; + const double kEpsilon = 1.0e-15; + // Default gyroscope frequency. This corresponds to 100 Hz. + const double kDefaultGyroscopeTimestep_s = 0.01f; + // Maximum time between gyroscope before we start limiting the integration. + const double kMaximumGyroscopeSampleDelay_s = 0.04f; + // Compute a first-order exponential moving average of changes in accel norm per + // frame. + const double kSmoothingFactor = 0.5; + // Minimum and maximum values used for accelerometer noise covariance matrix. + // The smaller the sigma value, the more weight is given to the accelerometer + // signal. + const double kMinAccelNoiseSigma = 0.75; + const double kMaxAccelNoiseSigma = 7.0; + // Initial value for the diagonal elements of the different covariance matrices. + const double kInitialStateCovarianceValue = 25.0; + const double kInitialProcessCovarianceValue = 1.0; + // Maximum accelerometer norm change allowed before capping it covariance to a + // large value. + const double kMaxAccelNormChange = 0.15; + // Timestep IIR filtering coefficient. + const double kTimestepFilterCoeff = 0.95; + // Minimum number of sample for timestep filtering. + const int kTimestepFilterMinSamples = 10; + + // Z direction in start space. + const Vector3 kCanonicalZDirection(0.0, 0.0, 1.0); + + // Computes an axis-angle rotation from the input vector. + // angle = norm(a) + // axis = a.normalized() + // If norm(a) == 0, it returns an identity rotation. + static inline Rotation RotationFromVector(const Vector3& a) + { + const double norm_a = Length(a); + if (norm_a < kEpsilon) { + return Rotation::Identity(); + } + return Rotation::FromAxisAndAngle(a / norm_a, norm_a); + } + +} // namespace + +SensorFusionEkf::SensorFusionEkf() + : execute_reset_with_next_accelerometer_sample_(false) + , bias_estimation_enabled_(true) + , gyroscope_bias_estimate_({ 0, 0, 0 }) +{ + ResetState(); +} + +void SensorFusionEkf::Reset() { execute_reset_with_next_accelerometer_sample_ = true; } + +void SensorFusionEkf::ResetState() +{ + current_state_.sensor_from_start_rotation = Rotation::Identity(); + current_state_.sensor_from_start_rotation_velocity = Vector3::Zero(); + + current_gyroscope_sensor_timestamp_ns_ = 0; + current_accelerometer_sensor_timestamp_ns_ = 0; + + state_covariance_ = Matrix3x3::Identity() * kInitialStateCovarianceValue; + process_covariance_ = Matrix3x3::Identity() * kInitialProcessCovarianceValue; + accelerometer_measurement_covariance_ + = Matrix3x3::Identity() * kMinAccelNoiseSigma * kMinAccelNoiseSigma; + innovation_covariance_ = Matrix3x3::Identity(); + + accelerometer_measurement_jacobian_ = Matrix3x3::Zero(); + kalman_gain_ = Matrix3x3::Zero(); + innovation_ = Vector3::Zero(); + accelerometer_measurement_ = Vector3::Zero(); + prediction_ = Vector3::Zero(); + control_input_ = Vector3::Zero(); + state_update_ = Vector3::Zero(); + + moving_average_accelerometer_norm_change_ = 0.0; + + is_timestep_filter_initialized_ = false; + is_gyroscope_filter_valid_ = false; + is_aligned_with_gravity_ = false; + + // Reset biases. + gyroscope_bias_estimator_.Reset(); + gyroscope_bias_estimate_ = { 0, 0, 0 }; +} + +// Here I am doing something wrong relative to time stamps. The state timestamps +// always correspond to the gyrostamps because it would require additional +// extrapolation if I wanted to do otherwise. +PoseState SensorFusionEkf::GetLatestPoseState() const { return current_state_; } + +void SensorFusionEkf::ProcessGyroscopeSample(const GyroscopeData& sample) +{ + // Don't accept gyroscope sample when waiting for a reset. + if (execute_reset_with_next_accelerometer_sample_) { + return; + } + + // Discard outdated samples. + if (current_gyroscope_sensor_timestamp_ns_ >= sample.sensor_timestamp_ns) { + current_gyroscope_sensor_timestamp_ns_ = sample.sensor_timestamp_ns; + return; + } + + // Checks that we received at least one gyroscope sample in the past. + if (current_gyroscope_sensor_timestamp_ns_ != 0) { + double current_timestep_s = std::chrono::duration_cast>( + std::chrono::nanoseconds( + sample.sensor_timestamp_ns - current_gyroscope_sensor_timestamp_ns_)) + .count(); + if (current_timestep_s > kMaximumGyroscopeSampleDelay_s) { + if (is_gyroscope_filter_valid_) { + // Replaces the delta timestamp by the filtered estimates of the delta time. + current_timestep_s = filtered_gyroscope_timestep_s_; + } else { + current_timestep_s = kDefaultGyroscopeTimestep_s; + } + } else { + FilterGyroscopeTimestep(current_timestep_s); + } + + if (bias_estimation_enabled_) { + gyroscope_bias_estimator_.ProcessGyroscope(sample.data, sample.sensor_timestamp_ns); + + if (gyroscope_bias_estimator_.IsCurrentEstimateValid()) { + // As soon as the device is considered to be static, the bias estimator + // should have a precise estimate of the gyroscope bias. + gyroscope_bias_estimate_ = gyroscope_bias_estimator_.GetGyroscopeBias(); + } + } + + // Only integrate after receiving an accelerometer sample. + if (is_aligned_with_gravity_) { + const Rotation rotation_from_gyroscope = pose_prediction::GetRotationFromGyroscope( + { sample.data[0] - gyroscope_bias_estimate_[0], + sample.data[1] - gyroscope_bias_estimate_[1], + sample.data[2] - gyroscope_bias_estimate_[2] }, + current_timestep_s); + current_state_.sensor_from_start_rotation + = rotation_from_gyroscope * current_state_.sensor_from_start_rotation; + UpdateStateCovariance(RotationMatrixNH(rotation_from_gyroscope)); + state_covariance_ = state_covariance_ + + ((current_timestep_s * current_timestep_s) * process_covariance_); + } + } + + // Saves gyroscope event for future prediction. + current_state_.timestamp = sample.system_timestamp; + current_gyroscope_sensor_timestamp_ns_ = sample.sensor_timestamp_ns; + current_state_.sensor_from_start_rotation_velocity.Set( + sample.data[0] - gyroscope_bias_estimate_[0], sample.data[1] - gyroscope_bias_estimate_[1], + sample.data[2] - gyroscope_bias_estimate_[2]); +} + +Vector3 SensorFusionEkf::ComputeInnovation(const Rotation& pose) +{ + const Vector3 predicted_down_direction = pose * kCanonicalZDirection; + + const Rotation rotation + = Rotation::RotateInto(predicted_down_direction, accelerometer_measurement_); + Vector3 axis; + double angle; + rotation.GetAxisAndAngle(&axis, &angle); + return axis * angle; +} + +void SensorFusionEkf::ComputeMeasurementJacobian() +{ + for (int dof = 0; dof < 3; dof++) { + Vector3 delta = Vector3::Zero(); + delta[dof] = kFiniteDifferencingEpsilon; + + const Rotation epsilon_rotation = RotationFromVector(delta); + const Vector3 delta_rotation + = ComputeInnovation(epsilon_rotation * current_state_.sensor_from_start_rotation); + + const Vector3 col = (innovation_ - delta_rotation) / kFiniteDifferencingEpsilon; + accelerometer_measurement_jacobian_(0, dof) = col[0]; + accelerometer_measurement_jacobian_(1, dof) = col[1]; + accelerometer_measurement_jacobian_(2, dof) = col[2]; + } +} + +void SensorFusionEkf::ProcessAccelerometerSample(const AccelerometerData& sample) +{ + // Discard outdated samples. + if (current_accelerometer_sensor_timestamp_ns_ >= sample.sensor_timestamp_ns) { + current_accelerometer_sensor_timestamp_ns_ = sample.sensor_timestamp_ns; + return; + } + + // Call reset state if required. + if (execute_reset_with_next_accelerometer_sample_.exchange(false)) { + ResetState(); + } + + accelerometer_measurement_.Set(sample.data[0], sample.data[1], sample.data[2]); + current_accelerometer_sensor_timestamp_ns_ = sample.sensor_timestamp_ns; + + if (bias_estimation_enabled_) { + gyroscope_bias_estimator_.ProcessAccelerometer(sample.data, sample.sensor_timestamp_ns); + } + + if (!is_aligned_with_gravity_) { + // This is the first accelerometer measurement so it initializes the + // orientation estimate. + current_state_.sensor_from_start_rotation + = Rotation::RotateInto(kCanonicalZDirection, accelerometer_measurement_); + is_aligned_with_gravity_ = true; + + previous_accelerometer_norm_ = Length(accelerometer_measurement_); + return; + } + + UpdateMeasurementCovariance(); + + innovation_ = ComputeInnovation(current_state_.sensor_from_start_rotation); + ComputeMeasurementJacobian(); + + // S = H * P * H' + R + innovation_covariance_ = accelerometer_measurement_jacobian_ * state_covariance_ + * Transpose(accelerometer_measurement_jacobian_) + + accelerometer_measurement_covariance_; + + // K = P * H' * S^-1 + kalman_gain_ = state_covariance_ * Transpose(accelerometer_measurement_jacobian_) + * Inverse(innovation_covariance_); + + // x_update = K*nu + state_update_ = kalman_gain_ * innovation_; + + // P = (I - K * H) * P; + state_covariance_ = (Matrix3x3::Identity() - kalman_gain_ * accelerometer_measurement_jacobian_) + * state_covariance_; + + // Updates pose and associate covariance matrix. + const Rotation rotation_from_state_update = RotationFromVector(state_update_); + + current_state_.sensor_from_start_rotation + = rotation_from_state_update * current_state_.sensor_from_start_rotation; + UpdateStateCovariance(RotationMatrixNH(rotation_from_state_update)); +} + +void SensorFusionEkf::UpdateStateCovariance(const Matrix3x3& motion_update) +{ + state_covariance_ = motion_update * state_covariance_ * Transpose(motion_update); +} + +void SensorFusionEkf::FilterGyroscopeTimestep(double gyroscope_timestep_s) +{ + if (!is_timestep_filter_initialized_) { + // Initializes the filter. + filtered_gyroscope_timestep_s_ = gyroscope_timestep_s; + num_gyroscope_timestep_samples_ = 1; + is_timestep_filter_initialized_ = true; + return; + } + + // Computes the IIR filter response. + filtered_gyroscope_timestep_s_ = kTimestepFilterCoeff * filtered_gyroscope_timestep_s_ + + (1 - kTimestepFilterCoeff) * gyroscope_timestep_s; + ++num_gyroscope_timestep_samples_; + + if (num_gyroscope_timestep_samples_ > kTimestepFilterMinSamples) { + is_gyroscope_filter_valid_ = true; + } +} + +void SensorFusionEkf::UpdateMeasurementCovariance() +{ + const double current_accelerometer_norm = Length(accelerometer_measurement_); + // Norm change between current and previous accel readings. + const double current_accelerometer_norm_change + = std::abs(current_accelerometer_norm - previous_accelerometer_norm_); + previous_accelerometer_norm_ = current_accelerometer_norm; + + moving_average_accelerometer_norm_change_ = kSmoothingFactor * current_accelerometer_norm_change + + (1 - kSmoothingFactor) * moving_average_accelerometer_norm_change_; + + // If we hit the accel norm change threshold, we use the maximum noise sigma + // for the accel covariance. For anything below that, we use a linear + // combination between min and max sigma values. + const double norm_change_ratio + = moving_average_accelerometer_norm_change_ / kMaxAccelNormChange; + const double accelerometer_noise_sigma = std::min(kMaxAccelNoiseSigma, + kMinAccelNoiseSigma + norm_change_ratio * (kMaxAccelNoiseSigma - kMinAccelNoiseSigma)); + + // Updates the accel covariance matrix with the new sigma value. + accelerometer_measurement_covariance_ + = Matrix3x3::Identity() * accelerometer_noise_sigma * accelerometer_noise_sigma; +} + +bool SensorFusionEkf::IsBiasEstimationEnabled() const { return bias_estimation_enabled_; } + +void SensorFusionEkf::SetBiasEstimationEnabled(bool enable) +{ + if (bias_estimation_enabled_ != enable) { + bias_estimation_enabled_ = enable; + gyroscope_bias_estimate_ = { 0, 0, 0 }; + gyroscope_bias_estimator_.Reset(); + } +} + +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/sensor_fusion_ekf.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/sensor_fusion_ekf.h new file mode 100644 index 000000000..a66fe33f4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/sensors/sensor_fusion_ekf.h @@ -0,0 +1,188 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_SENSORS_SENSOR_FUSION_EKF_H_ +#define CARDBOARD_SDK_SENSORS_SENSOR_FUSION_EKF_H_ + +#include +#include +#include + +#include "accelerometer_data.h" +#include "gyroscope_bias_estimator.h" +#include "gyroscope_data.h" +#include "pose_state.h" +#include "../util/matrix_3x3.h" +#include "../util/rotation.h" +#include "../util/vector.h" + +namespace cardboard { + +// Sensor fusion class that implements an Extended Kalman Filter (EKF) to +// estimate a 3D rotation from a gyroscope and an accelerometer. +// This system only has one state, the pose. It does not estimate any velocity +// or acceleration. +// +// To learn more about Kalman filtering one can read this article which is a +// good introduction: https://en.wikipedia.org/wiki/Kalman_filter +class SensorFusionEkf { +public: + SensorFusionEkf(); + + // Resets the state of the sensor fusion. It sets the velocity for + // prediction to zero. The reset will happen with the next + // accelerometer sample. Gyroscope sample will be discarded until a new + // accelerometer sample arrives. + void Reset(); + + // Gets the PoseState representing the latest pose and derivatives at a + // particular timestamp as estimated by SensorFusion. + PoseState GetLatestPoseState() const; + + // Processes one gyroscope sample event. This updates the pose of the system + // and the prediction model. The gyroscope data is assumed to be in axis angle + // form. Angle = ||v|| and Axis = v / ||v||, with v = [v_x, v_y, v_z]^T. + // + // @param sample gyroscope sample data. + void ProcessGyroscopeSample(const GyroscopeData& sample); + + // Processes one accelerometer sample event. This updates the pose of the + // system. If the Accelerometer norm changes too much between sample it is not + // trusted as much. + // + // @param sample accelerometer sample data. + void ProcessAccelerometerSample(const AccelerometerData& sample); + + // Enables or disables the drift correction by estimating the gyroscope bias. + // + // @param enable Enable drift correction. + void SetBiasEstimationEnabled(bool enable); + + // Returns a boolean that indicates if bias estimation is enabled or disabled. + // + // @return true if bias estimation is enabled, false otherwise. + bool IsBiasEstimationEnabled() const; + + // Returns the current gyroscope bias estimate from GyroscopeBiasEstimator. + Vector3 GetGyroscopeBias() const { + return { + gyroscope_bias_estimate_[0], gyroscope_bias_estimate_[1], gyroscope_bias_estimate_[2]}; + } + + // Returns true after receiving the first accelerometer measurement. + bool IsFullyInitialized() const { + return is_aligned_with_gravity_; + } + +private: + // Estimates the average timestep between gyroscope event. + void FilterGyroscopeTimestep(double gyroscope_timestep); + + // Updates the state covariance with an incremental motion. It changes the + // space of the quadric. + void UpdateStateCovariance(const Matrix3x3& motion_update); + + // Computes the innovation vector of the Kalman based on the input pose. + // It uses the latest measurement vector (i.e. accelerometer data), which must + // be set prior to calling this function. + Vector3 ComputeInnovation(const Rotation& pose); + + // This computes the measurement_jacobian_ via numerical differentiation based + // on the current value of sensor_from_start_rotation_. + void ComputeMeasurementJacobian(); + + // Updates the accelerometer covariance matrix. + // + // This looks at the norm of recent accelerometer readings. If it has changed + // significantly, it means the phone receives additional acceleration than + // just gravity, and so the down vector information gravity signal is noisier. + void UpdateMeasurementCovariance(); + + // Reset all internal states. This is not thread safe. Lock should be acquired + // outside of it. This function is called in ProcessAccelerometerSample. + void ResetState(); + + // Current transformation from Sensor Space to Start Space. + // x_sensor = sensor_from_start_rotation_ * x_start; + PoseState current_state_; + + // Filtering of the gyroscope timestep started? + bool is_timestep_filter_initialized_; + // Filtered gyroscope timestep valid? + bool is_gyroscope_filter_valid_; + // Sensor fusion currently aligned with gravity? After initialization + // it will requires a couple of accelerometer data for the system to get + // aligned. + std::atomic is_aligned_with_gravity_; + + // Covariance of Kalman filter state (P in common formulation). + Matrix3x3 state_covariance_; + // Covariance of the process noise (Q in common formulation). + Matrix3x3 process_covariance_; + // Covariance of the accelerometer measurement (R in common formulation). + Matrix3x3 accelerometer_measurement_covariance_; + // Covariance of innovation (S in common formulation). + Matrix3x3 innovation_covariance_; + // Jacobian of the measurements (H in common formulation). + Matrix3x3 accelerometer_measurement_jacobian_; + // Gain of the Kalman filter (K in common formulation). + Matrix3x3 kalman_gain_; + // Parameter update a.k.a. innovation vector. (\nu in common formulation). + Vector3 innovation_; + // Measurement vector (z in common formulation). + Vector3 accelerometer_measurement_; + // Current prediction vector (g in common formulation). + Vector3 prediction_; + // Control input, currently this is only the gyroscope data (\mu in common + // formulation). + Vector3 control_input_; + // Update of the state vector. (x in common formulation). + Vector3 state_update_; + + // Sensor time of the last gyroscope processed event. + uint64_t current_gyroscope_sensor_timestamp_ns_; + // Sensor time of the last accelerometer processed event. + uint64_t current_accelerometer_sensor_timestamp_ns_; + + // Estimates of the timestep between gyroscope event in seconds. + double filtered_gyroscope_timestep_s_; + // Number of timestep samples processed so far by the filter. + uint32_t num_gyroscope_timestep_samples_; + // Norm of the accelerometer for the previous measurement. + double previous_accelerometer_norm_; + // Moving average of the accelerometer norm changes. It is computed for every + // sensor datum. + double moving_average_accelerometer_norm_change_; + + // Flag indicating if a state reset should be executed with the next + // accelerometer sample. + std::atomic execute_reset_with_next_accelerometer_sample_; + + // Flag indicating if bias estimation is enabled (enabled by default). + std::atomic bias_estimation_enabled_; + + // Bias estimator and static device detector. + GyroscopeBiasEstimator gyroscope_bias_estimator_; + + // Current bias estimate_; + Vector3 gyroscope_bias_estimate_; + + SensorFusionEkf(const SensorFusionEkf&) = delete; + SensorFusionEkf& operator=(const SensorFusionEkf&) = delete; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_SENSORS_SENSOR_FUSION_EKF_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/logging.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/logging.h new file mode 100644 index 000000000..dee224b1c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/logging.h @@ -0,0 +1,38 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_LOGGING_H_ +#define CARDBOARD_SDK_UTIL_LOGGING_H_ + +#include +#include + +#if defined(__ANDROID__) + +#include + +// Uncomment these to enable debug logging from native code + +#define CARDBOARD_LOGI(...) // __android_log_print(ANDROID_LOG_INFO, "CardboardSDK", __VA_ARGS__) +#define CARDBOARD_LOGE(...) // __android_log_print(ANDROID_LOG_ERROR, "CardboardSDK", __VA_ARGS__) + +#else + +#define CARDBOARD_LOGI(...) // FURI_LOG_I("CardboardSDK", __VA_ARGS__) +#define CARDBOARD_LOGE(...) // FURI_LOG_E("CardboardSDK", __VA_ARGS__) + +#endif + +#endif // CARDBOARD_SDK_UTIL_LOGGING_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrix_3x3.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrix_3x3.cc new file mode 100644 index 000000000..9ddd847b0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrix_3x3.cc @@ -0,0 +1,121 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "matrix_3x3.h" + +namespace cardboard { + +Matrix3x3::Matrix3x3(double m00, double m01, double m02, double m10, double m11, double m12, + double m20, double m21, double m22) + : elem_ { { { m00, m01, m02 }, { m10, m11, m12 }, { m20, m21, m22 } } } +{ +} + +Matrix3x3::Matrix3x3() +{ + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + elem_[row][col] = 0; + } +} + +Matrix3x3 Matrix3x3::Zero() +{ + Matrix3x3 result; + return result; +} + +Matrix3x3 Matrix3x3::Identity() +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + result.elem_[row][row] = 1; + } + return result; +} + +void Matrix3x3::MultiplyScalar(double s) +{ + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + elem_[row][col] *= s; + } +} + +Matrix3x3 Matrix3x3::Negation() const +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result.elem_[row][col] = -elem_[row][col]; + } + return result; +} + +Matrix3x3 Matrix3x3::Scale(const Matrix3x3& m, double s) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result.elem_[row][col] = m.elem_[row][col] * s; + } + return result; +} + +Matrix3x3 Matrix3x3::Addition(const Matrix3x3& lhs, const Matrix3x3& rhs) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result.elem_[row][col] = lhs.elem_[row][col] + rhs.elem_[row][col]; + } + return result; +} + +Matrix3x3 Matrix3x3::Subtraction(const Matrix3x3& lhs, const Matrix3x3& rhs) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result.elem_[row][col] = lhs.elem_[row][col] - rhs.elem_[row][col]; + } + return result; +} + +Matrix3x3 Matrix3x3::Product(const Matrix3x3& m0, const Matrix3x3& m1) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + result.elem_[row][col] = 0; + for (int i = 0; i < 3; ++i) + result.elem_[row][col] += m0.elem_[row][i] * m1.elem_[i][col]; + } + } + return result; +} + +bool Matrix3x3::AreEqual(const Matrix3x3& m0, const Matrix3x3& m1) +{ + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + if (m0.elem_[row][col] != m1.elem_[row][col]) + return false; + } + } + return true; +} + +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrix_3x3.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrix_3x3.h new file mode 100644 index 000000000..81e4f2158 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrix_3x3.h @@ -0,0 +1,138 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_MATRIX_3X3_H_ +#define CARDBOARD_SDK_UTIL_MATRIX_3X3_H_ + +#include +#include // For memcpy(). +#include // NOLINT +#include // NOLINT + +namespace cardboard { + +// The Matrix3x3 class defines a square 3-dimensional matrix. Elements are +// stored in row-major order. +// TODO(b/135461889): Make this class consistent with Matrix4x4. +class Matrix3x3 { +public: + // The default constructor zero-initializes all elements. + Matrix3x3(); + + // Dimension-specific constructors that are passed individual element values. + Matrix3x3( + double m00, + double m01, + double m02, + double m10, + double m11, + double m12, + double m20, + double m21, + double m22); + + // Constructor that reads elements from a linear array of the correct size. + explicit Matrix3x3(const double array[3 * 3]); + + // Returns a Matrix3x3 containing all zeroes. + static Matrix3x3 Zero(); + + // Returns an identity Matrix3x3. + static Matrix3x3 Identity(); + + // Mutable element accessors. + double& operator()(int row, int col) { + return elem_[row][col]; + } + std::array& operator[](int row) { + return elem_[row]; + } + + // Read-only element accessors. + const double& operator()(int row, int col) const { + return elem_[row][col]; + } + const std::array& operator[](int row) const { + return elem_[row]; + } + + // Return a pointer to the data for interfacing with libraries. + double* Data() { + return &elem_[0][0]; + } + const double* Data() const { + return &elem_[0][0]; + } + + // Self-modifying multiplication operators. + void operator*=(double s) { + MultiplyScalar(s); + } + void operator*=(const Matrix3x3& m) { + *this = Product(*this, m); + } + + // Unary operators. + Matrix3x3 operator-() const { + return Negation(); + } + + // Binary scale operators. + friend Matrix3x3 operator*(const Matrix3x3& m, double s) { + return Scale(m, s); + } + friend Matrix3x3 operator*(double s, const Matrix3x3& m) { + return Scale(m, s); + } + + // Binary matrix addition. + friend Matrix3x3 operator+(const Matrix3x3& lhs, const Matrix3x3& rhs) { + return Addition(lhs, rhs); + } + + // Binary matrix subtraction. + friend Matrix3x3 operator-(const Matrix3x3& lhs, const Matrix3x3& rhs) { + return Subtraction(lhs, rhs); + } + + // Binary multiplication operator. + friend Matrix3x3 operator*(const Matrix3x3& m0, const Matrix3x3& m1) { + return Product(m0, m1); + } + + // Exact equality and inequality comparisons. + friend bool operator==(const Matrix3x3& m0, const Matrix3x3& m1) { + return AreEqual(m0, m1); + } + friend bool operator!=(const Matrix3x3& m0, const Matrix3x3& m1) { + return !AreEqual(m0, m1); + } + +private: + // These private functions implement most of the operators. + void MultiplyScalar(double s); + Matrix3x3 Negation() const; + static Matrix3x3 Addition(const Matrix3x3& lhs, const Matrix3x3& rhs); + static Matrix3x3 Subtraction(const Matrix3x3& lhs, const Matrix3x3& rhs); + static Matrix3x3 Scale(const Matrix3x3& m, double s); + static Matrix3x3 Product(const Matrix3x3& m0, const Matrix3x3& m1); + static bool AreEqual(const Matrix3x3& m0, const Matrix3x3& m1); + + std::array, 3> elem_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_MATRIX_3X3_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrix_4x4.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrix_4x4.cc new file mode 100644 index 000000000..8db3cbc5b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrix_4x4.cc @@ -0,0 +1,87 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "matrix_4x4.h" + +#include +#include +#include + +namespace cardboard { + +Matrix4x4 Matrix4x4::Identity() +{ + Matrix4x4 ret; + for (int j = 0; j < 4; ++j) { + for (int i = 0; i < 4; ++i) { + ret.m[j][i] = (i == j) ? 1 : 0; + } + } + + return ret; +} + +Matrix4x4 Matrix4x4::Zeros() +{ + Matrix4x4 ret; + for (int j = 0; j < 4; ++j) { + for (int i = 0; i < 4; ++i) { + ret.m[j][i] = 0; + } + } + + return ret; +} + +Matrix4x4 Matrix4x4::Translation(float x, float y, float z) +{ + Matrix4x4 ret = Matrix4x4::Identity(); + ret.m[3][0] = x; + ret.m[3][1] = y; + ret.m[3][2] = z; + + return ret; +} + +Matrix4x4 Matrix4x4::Perspective(const std::array& fov, float zNear, float zFar) +{ + Matrix4x4 ret = Matrix4x4::Zeros(); + + const float xLeft = -std::tan(fov[0] * M_PI / 180.0f) * zNear; + const float xRight = std::tan(fov[1] * M_PI / 180.0f) * zNear; + const float yBottom = -std::tan(fov[2] * M_PI / 180.0f) * zNear; + const float yTop = std::tan(fov[3] * M_PI / 180.0f) * zNear; + + const float X = (2 * zNear) / (xRight - xLeft); + const float Y = (2 * zNear) / (yTop - yBottom); + const float A = (xRight + xLeft) / (xRight - xLeft); + const float B = (yTop + yBottom) / (yTop - yBottom); + const float C = (zNear + zFar) / (zNear - zFar); + const float D = (2 * zNear * zFar) / (zNear - zFar); + + ret.m[0][0] = X; + ret.m[2][0] = A; + ret.m[1][1] = Y; + ret.m[2][1] = B; + ret.m[2][2] = C; + ret.m[3][2] = D; + ret.m[2][3] = -1; + + return ret; +} + +void Matrix4x4::ToArray(float* array) const { std::memcpy(array, &m[0][0], 16 * sizeof(float)); } + +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrix_4x4.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrix_4x4.h new file mode 100644 index 000000000..9934f6be0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrix_4x4.h @@ -0,0 +1,37 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_MATRIX_4X4_H_ +#define CARDBOARD_SDK_UTIL_MATRIX_4X4_H_ + +#include + +namespace cardboard { + +class Matrix4x4 { +public: + static Matrix4x4 Identity(); + static Matrix4x4 Zeros(); + static Matrix4x4 Translation(float x, float y, float z); + static Matrix4x4 Perspective(const std::array& fov, float zNear, float zFar); + void ToArray(float* array) const; + +private: + std::array, 4> m; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_MATRIX4X4_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrixutils.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrixutils.cc new file mode 100644 index 000000000..12470beae --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrixutils.cc @@ -0,0 +1,148 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "matrixutils.h" + +#include "vectorutils.h" + +namespace cardboard { + +namespace { + + // Returns true if the cofactor for a given row and column should be negated. + static bool IsCofactorNegated(int row, int col) + { + // Negated iff (row + col) is odd. + return ((row + col) & 1) != 0; + } + + static double CofactorElement3(const Matrix3x3& m, int row, int col) + { + static const int index[3][2] = { { 1, 2 }, { 0, 2 }, { 0, 1 } }; + const int i0 = index[row][0]; + const int i1 = index[row][1]; + const int j0 = index[col][0]; + const int j1 = index[col][1]; + const double cofactor = m(i0, j0) * m(i1, j1) - m(i0, j1) * m(i1, j0); + return IsCofactorNegated(row, col) ? -cofactor : cofactor; + } + + // Multiplies a matrix and some type of column vector to + // produce another column vector of the same type. + Vector3 MultiplyMatrixAndVector(const Matrix3x3& m, const Vector3& v) + { + Vector3 result = Vector3::Zero(); + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result[row] += m(row, col) * v[col]; + } + return result; + } + + // Sets the upper 3x3 of a Matrix to represent a 3D rotation. + void RotationMatrix3x3(const Rotation& r, Matrix3x3* matrix) + { + // + // Given a quaternion (a,b,c,d) where d is the scalar part, the 3x3 rotation + // matrix is: + // + // a^2 - b^2 - c^2 + d^2 2ab - 2cd 2ac + 2bd + // 2ab + 2cd -a^2 + b^2 - c^2 + d^2 2bc - 2ad + // 2ac - 2bd 2bc + 2ad -a^2 - b^2 + c^2 + d^2 + // + const Vector<4>& quat = r.GetQuaternion(); + const double aa = quat[0] * quat[0]; + const double bb = quat[1] * quat[1]; + const double cc = quat[2] * quat[2]; + const double dd = quat[3] * quat[3]; + + const double ab = quat[0] * quat[1]; + const double ac = quat[0] * quat[2]; + const double bc = quat[1] * quat[2]; + + const double ad = quat[0] * quat[3]; + const double bd = quat[1] * quat[3]; + const double cd = quat[2] * quat[3]; + + Matrix3x3& m = *matrix; + m[0][0] = aa - bb - cc + dd; + m[0][1] = 2 * ab - 2 * cd; + m[0][2] = 2 * ac + 2 * bd; + m[1][0] = 2 * ab + 2 * cd; + m[1][1] = -aa + bb - cc + dd; + m[1][2] = 2 * bc - 2 * ad; + m[2][0] = 2 * ac - 2 * bd; + m[2][1] = 2 * bc + 2 * ad; + m[2][2] = -aa - bb + cc + dd; + } + +} // anonymous namespace + +Vector3 operator*(const Matrix3x3& m, const Vector3& v) { return MultiplyMatrixAndVector(m, v); } + +Matrix3x3 CofactorMatrix(const Matrix3x3& m) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result(row, col) = CofactorElement3(m, row, col); + } + return result; +} + +Matrix3x3 AdjugateWithDeterminant(const Matrix3x3& m, double* determinant) +{ + const Matrix3x3 cofactor_matrix = CofactorMatrix(m); + if (determinant) { + *determinant = m(0, 0) * cofactor_matrix(0, 0) + m(0, 1) * cofactor_matrix(0, 1) + + m(0, 2) * cofactor_matrix(0, 2); + } + return Transpose(cofactor_matrix); +} + +// Returns the transpose of a matrix. +Matrix3x3 Transpose(const Matrix3x3& m) +{ + Matrix3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) + result(row, col) = m(col, row); + } + return result; +} + +Matrix3x3 InverseWithDeterminant(const Matrix3x3& m, double* determinant) +{ + // The inverse is the adjugate divided by the determinant. + double det; + Matrix3x3 adjugate = AdjugateWithDeterminant(m, &det); + if (determinant) + *determinant = det; + if (det == 0) + return Matrix3x3::Zero(); + else + return adjugate * (1 / det); +} + +Matrix3x3 Inverse(const Matrix3x3& m) { return InverseWithDeterminant(m, nullptr); } + +Matrix3x3 RotationMatrixNH(const Rotation& r) +{ + Matrix3x3 m; + RotationMatrix3x3(r, &m); + return m; +} + +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrixutils.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrixutils.h new file mode 100644 index 000000000..80f9b2168 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/matrixutils.h @@ -0,0 +1,65 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_MATRIXUTILS_H_ +#define CARDBOARD_SDK_UTIL_MATRIXUTILS_H_ + +// +// This file contains operators and free functions that define generic Matrix +// operations. +// + +#include "matrix_3x3.h" +#include "rotation.h" +#include "vector.h" + +namespace cardboard { + +// Returns the transpose of a matrix. +Matrix3x3 Transpose(const Matrix3x3& m); + +// Multiplies a Matrix and a column Vector of the same Dimension to produce +// another column Vector. +Vector3 operator*(const Matrix3x3& m, const Vector3& v); + +// Returns the determinant of the matrix. This function is defined for all the +// typedef'ed Matrix types. +double Determinant(const Matrix3x3& m); + +// Returns the adjugate of the matrix, which is defined as the transpose of the +// cofactor matrix. This function is defined for all the typedef'ed Matrix +// types. The determinant of the matrix is computed as a side effect, so it is +// returned in the determinant parameter if it is not null. +Matrix3x3 AdjugateWithDeterminant(const Matrix3x3& m, double* determinant); + +// Returns the inverse of the matrix. This function is defined for all the +// typedef'ed Matrix types. The determinant of the matrix is computed as a +// side effect, so it is returned in the determinant parameter if it is not +// null. If the determinant is 0, the returned matrix has all zeroes. +Matrix3x3 InverseWithDeterminant(const Matrix3x3& m, double* determinant); + +// Returns the inverse of the matrix. This function is defined for all the +// typedef'ed Matrix types. If the determinant of the matrix is 0, the returned +// matrix has all zeroes. +Matrix3x3 Inverse(const Matrix3x3& m); + +// Returns a 3x3 Matrix representing a 3D rotation. This creates a Matrix that +// does not work with homogeneous coordinates, so the function name ends in +// "NH". +Matrix3x3 RotationMatrixNH(const Rotation& r); + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_MATRIXUTILS_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/rotation.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/rotation.cc new file mode 100644 index 000000000..5c3d09a2b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/rotation.cc @@ -0,0 +1,117 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "rotation.h" + +#include +#include + +#include "vectorutils.h" + +namespace cardboard { + +void Rotation::SetAxisAndAngle(const VectorType& axis, double angle) +{ + VectorType unit_axis = axis; + if (!Normalize(&unit_axis)) { + *this = Identity(); + } else { + double a = angle / 2; + const double s = sin(a); + const VectorType v(unit_axis * s); + SetQuaternion(QuaternionType(v[0], v[1], v[2], cos(a))); + } +} + +Rotation Rotation::FromRotationMatrix(const Matrix3x3& mat) +{ + static const double kOne = 1.0; + static const double kFour = 4.0; + + const double d0 = mat(0, 0), d1 = mat(1, 1), d2 = mat(2, 2); + const double ww = kOne + d0 + d1 + d2; + const double xx = kOne + d0 - d1 - d2; + const double yy = kOne - d0 + d1 - d2; + const double zz = kOne - d0 - d1 + d2; + + const double max = std::max(ww, std::max(xx, std::max(yy, zz))); + if (ww == max) { + const double w4 = sqrt(ww * kFour); + return Rotation::FromQuaternion(QuaternionType((mat(2, 1) - mat(1, 2)) / w4, + (mat(0, 2) - mat(2, 0)) / w4, (mat(1, 0) - mat(0, 1)) / w4, w4 / kFour)); + } + + if (xx == max) { + const double x4 = sqrt(xx * kFour); + return Rotation::FromQuaternion(QuaternionType(x4 / kFour, (mat(0, 1) + mat(1, 0)) / x4, + (mat(0, 2) + mat(2, 0)) / x4, (mat(2, 1) - mat(1, 2)) / x4)); + } + + if (yy == max) { + const double y4 = sqrt(yy * kFour); + return Rotation::FromQuaternion(QuaternionType((mat(0, 1) + mat(1, 0)) / y4, y4 / kFour, + (mat(1, 2) + mat(2, 1)) / y4, (mat(0, 2) - mat(2, 0)) / y4)); + } + + // zz is the largest component. + const double z4 = sqrt(zz * kFour); + return Rotation::FromQuaternion(QuaternionType((mat(0, 2) + mat(2, 0)) / z4, + (mat(1, 2) + mat(2, 1)) / z4, z4 / kFour, (mat(1, 0) - mat(0, 1)) / z4)); +} + +void Rotation::GetAxisAndAngle(VectorType* axis, double* angle) const +{ + VectorType vec(quat_[0], quat_[1], quat_[2]); + if (Normalize(&vec)) { + *angle = 2 * acos(quat_[3]); + *axis = vec; + } else { + *axis = VectorType(1, 0, 0); + *angle = 0.0; + } +} + +Rotation Rotation::RotateInto(const VectorType& from, const VectorType& to) +{ + static const double kTolerance = std::numeric_limits::epsilon() * 100; + + // Directly build the quaternion using the following technique: + // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final + const double norm_u_norm_v = sqrt(LengthSquared(from) * LengthSquared(to)); + double real_part = norm_u_norm_v + Dot(from, to); + VectorType w; + if (real_part < kTolerance * norm_u_norm_v) { + // If |from| and |to| are exactly opposite, rotate 180 degrees around an + // arbitrary orthogonal axis. Axis normalization can happen later, when we + // normalize the quaternion. + real_part = 0.0; + w = (abs(from[0]) > abs(from[2])) ? VectorType(-from[1], from[0], 0) + : VectorType(0, -from[2], from[1]); + } else { + // Otherwise, build the quaternion the standard way. + w = Cross(from, to); + } + + // Build and return a normalized quaternion. + // Note that Rotation::FromQuaternion automatically performs normalization. + return Rotation::FromQuaternion(QuaternionType(w[0], w[1], w[2], real_part)); +} + +Rotation::VectorType Rotation::operator*(const Rotation::VectorType& v) const +{ + return ApplyToVector(v); +} + +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/rotation.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/rotation.h new file mode 100644 index 000000000..8730cb3b0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/rotation.h @@ -0,0 +1,156 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_ROTATION_H_ +#define CARDBOARD_SDK_UTIL_ROTATION_H_ + +#include "matrix_3x3.h" +#include "vector.h" +#include "vectorutils.h" + +namespace cardboard { + +// The Rotation class represents a rotation around a 3-dimensional axis. It +// uses normalized quaternions internally to make the math robust. +class Rotation { +public: + // Convenience typedefs for vector of the correct type. + typedef Vector<3> VectorType; + typedef Vector<4> QuaternionType; + + // The default constructor creates an identity Rotation, which has no effect. + Rotation() { + quat_.Set(0, 0, 0, 1); + } + + // Returns an identity Rotation, which has no effect. + static Rotation Identity() { + return Rotation(); + } + + // Sets the Rotation from a quaternion (4D vector), which is first normalized. + void SetQuaternion(const QuaternionType& quaternion) { + quat_ = Normalized(quaternion); + } + + // Returns the Rotation as a normalized quaternion (4D vector). + const QuaternionType& GetQuaternion() const { + return quat_; + } + + // Sets the Rotation to rotate by the given angle around the given axis, + // following the right-hand rule. The axis does not need to be unit + // length. If it is zero length, this results in an identity Rotation. + void SetAxisAndAngle(const VectorType& axis, double angle); + + // Returns the right-hand rule axis and angle corresponding to the + // Rotation. If the Rotation is the identity rotation, this returns the +X + // axis and an angle of 0. + void GetAxisAndAngle(VectorType* axis, double* angle) const; + + // Convenience function that constructs and returns a Rotation given an axis + // and angle. + static Rotation FromAxisAndAngle(const VectorType& axis, double angle) { + Rotation r; + r.SetAxisAndAngle(axis, angle); + return r; + } + + // Convenience function that constructs and returns a Rotation given a + // quaternion. + static Rotation FromQuaternion(const QuaternionType& quat) { + Rotation r; + r.SetQuaternion(quat); + return r; + } + + // Convenience function that constructs and returns a Rotation given a + // rotation matrix R with $R^\top R = I && det(R) = 1$. + static Rotation FromRotationMatrix(const Matrix3x3& mat); + + // Convenience function that constructs and returns a Rotation given Euler + // angles that are applied in the order of rotate-Z by roll, rotate-X by + // pitch, rotate-Y by yaw (same as GetRollPitchYaw). + static Rotation FromRollPitchYaw(double roll, double pitch, double yaw) { + VectorType x(1, 0, 0), y(0, 1, 0), z(0, 0, 1); + return FromAxisAndAngle(z, roll) * (FromAxisAndAngle(x, pitch) * FromAxisAndAngle(y, yaw)); + } + + // Convenience function that constructs and returns a Rotation given Euler + // angles that are applied in the order of rotate-Y by yaw, rotate-X by + // pitch, rotate-Z by roll (same as GetYawPitchRoll). + static Rotation FromYawPitchRoll(double yaw, double pitch, double roll) { + VectorType x(1, 0, 0), y(0, 1, 0), z(0, 0, 1); + return FromAxisAndAngle(y, yaw) * (FromAxisAndAngle(x, pitch) * FromAxisAndAngle(z, roll)); + } + + // Constructs and returns a Rotation that rotates one vector to another along + // the shortest arc. This returns an identity rotation if either vector has + // zero length. + static Rotation RotateInto(const VectorType& from, const VectorType& to); + + // The negation operator returns the inverse rotation. + friend Rotation operator-(const Rotation& r) { + // Because we store normalized quaternions, the inverse is found by + // negating the vector part. + return Rotation(-r.quat_[0], -r.quat_[1], -r.quat_[2], r.quat_[3]); + } + + // Appends a rotation to this one. + Rotation& operator*=(const Rotation& r) { + const QuaternionType& qr = r.quat_; + QuaternionType& qt = quat_; + SetQuaternion(QuaternionType( + qr[3] * qt[0] + qr[0] * qt[3] + qr[2] * qt[1] - qr[1] * qt[2], + qr[3] * qt[1] + qr[1] * qt[3] + qr[0] * qt[2] - qr[2] * qt[0], + qr[3] * qt[2] + qr[2] * qt[3] + qr[1] * qt[0] - qr[0] * qt[1], + qr[3] * qt[3] - qr[0] * qt[0] - qr[1] * qt[1] - qr[2] * qt[2])); + return *this; + } + + // Binary multiplication operator - returns a composite Rotation. + friend const Rotation operator*(const Rotation& r0, const Rotation& r1) { + Rotation r = r0; + r *= r1; + return r; + } + + // Multiply a Rotation and a Vector to get a Vector. + VectorType operator*(const VectorType& v) const; + +private: + // Private constructor that builds a Rotation from quaternion components. + Rotation(double q0, double q1, double q2, double q3) + : quat_(q0, q1, q2, q3) { + } + + // Applies a Rotation to a Vector to rotate the Vector. Method borrowed from: + // http://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/ + VectorType ApplyToVector(const VectorType& v) const { + VectorType im(quat_[0], quat_[1], quat_[2]); + VectorType temp = 2.0 * Cross(im, v); + return v + quat_[3] * temp + Cross(im, temp); + } + + // The rotation represented as a normalized quaternion. (Unit quaternions are + // required for constructing rotation matrices, so it makes sense to always + // store them that way.) The vector part is in the first 3 elements, and the + // scalar part is in the last element. + QuaternionType quat_; +}; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_ROTATION_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/vector.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/vector.h new file mode 100644 index 000000000..64c4f2546 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/vector.h @@ -0,0 +1,251 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_VECTOR_H_ +#define CARDBOARD_SDK_UTIL_VECTOR_H_ + +#include + +namespace cardboard { + +// Geometric N-dimensional Vector class. +template +class Vector { +public: + // The default constructor zero-initializes all elements. + Vector(); + + // Dimension-specific constructors that are passed individual element values. + constexpr Vector(double e0, double e1, double e2); + constexpr Vector(double e0, double e1, double e2, double e3); + + // Constructor for a Vector of dimension N from a Vector of dimension N-1 and + // a scalar of the correct type, assuming N is at least 2. + // constexpr Vector(const Vector& v, double s); + + void Set(double e0, double e1, double e2); // Only when Dimension == 3. + void Set(double e0, double e1, double e2, + double e3); // Only when Dimension == 4. + + // Mutable element accessor. + double& operator[](int index) { + return elem_[index]; + } + + // Element accessor. + double operator[](int index) const { + return elem_[index]; + } + + // Returns a Vector containing all zeroes. + static Vector Zero(); + + // Self-modifying operators. + void operator+=(const Vector& v) { + Add(v); + } + void operator-=(const Vector& v) { + Subtract(v); + } + void operator*=(double s) { + Multiply(s); + } + void operator/=(double s) { + Divide(s); + } + + // Unary negation operator. + Vector operator-() const { + return Negation(); + } + + // Binary operators. + friend Vector operator+(const Vector& v0, const Vector& v1) { + return Sum(v0, v1); + } + friend Vector operator-(const Vector& v0, const Vector& v1) { + return Difference(v0, v1); + } + friend Vector operator*(const Vector& v, double s) { + return Scale(v, s); + } + friend Vector operator*(double s, const Vector& v) { + return Scale(v, s); + } + friend Vector operator*(const Vector& v, const Vector& s) { + return Product(v, s); + } + friend Vector operator/(const Vector& v, double s) { + return Divide(v, s); + } + + // Self-modifying addition. + void Add(const Vector& v); + // Self-modifying subtraction. + void Subtract(const Vector& v); + // Self-modifying multiplication by a scalar. + void Multiply(double s); + // Self-modifying division by a scalar. + void Divide(double s); + + // Unary negation. + Vector Negation() const; + + // Binary component-wise multiplication. + static Vector Product(const Vector& v0, const Vector& v1); + // Binary component-wise addition. + static Vector Sum(const Vector& v0, const Vector& v1); + // Binary component-wise subtraction. + static Vector Difference(const Vector& v0, const Vector& v1); + // Binary multiplication by a scalar. + static Vector Scale(const Vector& v, double s); + // Binary division by a scalar. + static Vector Divide(const Vector& v, double s); + +private: + std::array elem_; +}; +//------------------------------------------------------------------------------ + +template +Vector::Vector() { + for(int i = 0; i < Dimension; i++) { + elem_[i] = 0; + } +} + +template +constexpr Vector::Vector(double e0, double e1, double e2) + : elem_{e0, e1, e2} { +} + +template +constexpr Vector::Vector(double e0, double e1, double e2, double e3) + : elem_{e0, e1, e2, e3} { +} +/* +template <> +constexpr Vector<4>::Vector(const Vector<3>& v, double s) + : elem_{v[0], v[1], v[2], s} {} +*/ +template +void Vector::Set(double e0, double e1, double e2) { + elem_[0] = e0; + elem_[1] = e1; + elem_[2] = e2; +} + +template +void Vector::Set(double e0, double e1, double e2, double e3) { + elem_[0] = e0; + elem_[1] = e1; + elem_[2] = e2; + elem_[3] = e3; +} + +template +Vector Vector::Zero() { + Vector v; + return v; +} + +template +void Vector::Add(const Vector& v) { + for(int i = 0; i < Dimension; i++) { + elem_[i] += v[i]; + } +} + +template +void Vector::Subtract(const Vector& v) { + for(int i = 0; i < Dimension; i++) { + elem_[i] -= v[i]; + } +} + +template +void Vector::Multiply(double s) { + for(int i = 0; i < Dimension; i++) { + elem_[i] *= s; + } +} + +template +void Vector::Divide(double s) { + for(int i = 0; i < Dimension; i++) { + elem_[i] /= s; + } +} + +template +Vector Vector::Negation() const { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = -elem_[i]; + } + return ret; +} + +template +Vector Vector::Product(const Vector& v0, const Vector& v1) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v0[i] * v1[i]; + } + return ret; +} + +template +Vector Vector::Sum(const Vector& v0, const Vector& v1) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v0[i] + v1[i]; + } + return ret; +} + +template +Vector Vector::Difference(const Vector& v0, const Vector& v1) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v0[i] - v1[i]; + } + return ret; +} + +template +Vector Vector::Scale(const Vector& v, double s) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v[i] * s; + } + return ret; +} + +template +Vector Vector::Divide(const Vector& v, double s) { + Vector ret; + for(int i = 0; i < Dimension; i++) { + ret.elem_[i] = v[i] / s; + } + return ret; +} + +typedef Vector<3> Vector3; +typedef Vector<4> Vector4; + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_VECTOR_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/vectorutils.cc b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/vectorutils.cc new file mode 100644 index 000000000..b8f419c04 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/vectorutils.cc @@ -0,0 +1,40 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "vectorutils.h" + +namespace cardboard { + +// Returns the dot (inner) product of two Vectors. +double Dot(const Vector<3>& v0, const Vector<3>& v1) +{ + return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2]; +} + +// Returns the dot (inner) product of two Vectors. +double Dot(const Vector<4>& v0, const Vector<4>& v1) +{ + return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2] + v0[3] * v1[3]; +} + +// Returns the 3-dimensional cross product of 2 Vectors. Note that this is +// defined only for 3-dimensional Vectors. +Vector<3> Cross(const Vector<3>& v0, const Vector<3>& v1) +{ + return Vector<3>(v0[1] * v1[2] - v0[2] * v1[1], v0[2] * v1[0] - v0[0] * v1[2], + v0[0] * v1[1] - v0[1] * v1[0]); +} + +} // namespace cardboard diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/vectorutils.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/vectorutils.h new file mode 100644 index 000000000..054236713 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/tracking/util/vectorutils.h @@ -0,0 +1,76 @@ +/* + * Copyright 2019 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CARDBOARD_SDK_UTIL_VECTORUTILS_H_ +#define CARDBOARD_SDK_UTIL_VECTORUTILS_H_ + +// +// This file contains free functions that operate on Vector instances. +// + +#include + +#include "vector.h" + +namespace cardboard { + +// Returns the dot (inner) product of two Vectors. +double Dot(const Vector<3>& v0, const Vector<3>& v1); + +// Returns the dot (inner) product of two Vectors. +double Dot(const Vector<4>& v0, const Vector<4>& v1); + +// Returns the 3-dimensional cross product of 2 Vectors. Note that this is +// defined only for 3-dimensional Vectors. +Vector<3> Cross(const Vector<3>& v0, const Vector<3>& v1); + +// Returns the square of the length of a Vector. +template +double LengthSquared(const Vector& v) { + return Dot(v, v); +} + +// Returns the geometric length of a Vector. +template +double Length(const Vector& v) { + return sqrt(LengthSquared(v)); +} + +// the Vector untouched and returns false. +template +bool Normalize(Vector* v) { + const double len = Length(*v); + if(len == 0) { + return false; + } else { + (*v) /= len; + return true; + } +} + +// Returns a unit-length version of a Vector. If the given Vector has no +// length, this returns a Zero() Vector. +template +Vector Normalized(const Vector& v) { + Vector result = v; + if(Normalize(&result)) + return result; + else + return Vector::Zero(); +} + +} // namespace cardboard + +#endif // CARDBOARD_SDK_UTIL_VECTORUTILS_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/bt_mouse.c b/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/bt_mouse.c new file mode 100644 index 000000000..7d9c0e6db --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/bt_mouse.c @@ -0,0 +1,310 @@ +#include "bt_mouse.h" +#include "../tracking/main_loop.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct ButtonEvent { + int8_t button; + bool state; +} ButtonEvent; + +#define BTN_EVT_QUEUE_SIZE 32 + +struct BtMouse { + View* view; + ViewDispatcher* view_dispatcher; + Bt* bt; + NotificationApp* notifications; + FuriMutex* mutex; + FuriThread* thread; + bool connected; + + // Current mouse state + uint8_t btn; + int dx; + int dy; + int wheel; + + // Circular buffer; + // (qhead == qtail) means either empty or overflow. + // We'll ignore overflow and treat it as empty. + int qhead; + int qtail; + ButtonEvent queue[BTN_EVT_QUEUE_SIZE]; +}; + +#define BT_MOUSE_FLAG_INPUT_EVENT (1UL << 0) +#define BT_MOUSE_FLAG_KILL_THREAD (1UL << 1) +#define BT_MOUSE_FLAG_ALL (BT_MOUSE_FLAG_INPUT_EVENT | BT_MOUSE_FLAG_KILL_THREAD) + +#define MOUSE_SCROLL 2 + +static void bt_mouse_notify_event(BtMouse* bt_mouse) { + FuriThreadId thread_id = furi_thread_get_id(bt_mouse->thread); + furi_assert(thread_id); + furi_thread_flags_set(thread_id, BT_MOUSE_FLAG_INPUT_EVENT); +} + +static void bt_mouse_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "Bluetooth Mouse mode"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); +} + +static void bt_mouse_button_state(BtMouse* bt_mouse, int8_t button, bool state) { + ButtonEvent event; + event.button = button; + event.state = state; + + if(bt_mouse->connected) { + furi_mutex_acquire(bt_mouse->mutex, FuriWaitForever); + bt_mouse->queue[bt_mouse->qtail++] = event; + bt_mouse->qtail %= BTN_EVT_QUEUE_SIZE; + furi_mutex_release(bt_mouse->mutex); + bt_mouse_notify_event(bt_mouse); + } +} + +static void bt_mouse_process(BtMouse* bt_mouse, InputEvent* event) { + with_view_model( + bt_mouse->view, + void* model, + { + UNUSED(model); + if(event->key == InputKeyUp) { + if(event->type == InputTypePress) { + bt_mouse_button_state(bt_mouse, HID_MOUSE_BTN_LEFT, true); + } else if(event->type == InputTypeRelease) { + bt_mouse_button_state(bt_mouse, HID_MOUSE_BTN_LEFT, false); + } + } else if(event->key == InputKeyDown) { + if(event->type == InputTypePress) { + bt_mouse_button_state(bt_mouse, HID_MOUSE_BTN_RIGHT, true); + } else if(event->type == InputTypeRelease) { + bt_mouse_button_state(bt_mouse, HID_MOUSE_BTN_RIGHT, false); + } + } else if(event->key == InputKeyOk) { + if(event->type == InputTypePress) { + bt_mouse_button_state(bt_mouse, HID_MOUSE_BTN_WHEEL, true); + } else if(event->type == InputTypeRelease) { + bt_mouse_button_state(bt_mouse, HID_MOUSE_BTN_WHEEL, false); + } + } else if(event->key == InputKeyRight) { + if(event->type == InputTypePress || event->type == InputTypeRepeat) { + bt_mouse->wheel = MOUSE_SCROLL; + } + } else if(event->key == InputKeyLeft) { + if(event->type == InputTypePress || event->type == InputTypeRepeat) { + bt_mouse->wheel = -MOUSE_SCROLL; + } + } + }, + true); +} + +static bool bt_mouse_input_callback(InputEvent* event, void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + furi_hal_bt_hid_mouse_release_all(); + } else { + bt_mouse_process(bt_mouse, event); + consumed = true; + } + + return consumed; +} + +void bt_mouse_connection_status_changed_callback(BtStatus status, void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + + bt_mouse->connected = (status == BtStatusConnected); + if(bt_mouse->connected) { + notification_internal_message(bt_mouse->notifications, &sequence_set_blue_255); + tracking_begin(); + view_dispatcher_send_custom_event(bt_mouse->view_dispatcher, 0); + } else { + tracking_end(); + notification_internal_message(bt_mouse->notifications, &sequence_reset_blue); + } + + //with_view_model( + // bt_mouse->view, void * model, { model->connected = connected; }, true); +} + +bool bt_mouse_move(int8_t dx, int8_t dy, void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + + if(bt_mouse->connected) { + furi_mutex_acquire(bt_mouse->mutex, FuriWaitForever); + bt_mouse->dx += dx; + bt_mouse->dy += dy; + furi_mutex_release(bt_mouse->mutex); + bt_mouse_notify_event(bt_mouse); + } + + return true; +} + +void bt_mouse_enter_callback(void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + + bt_mouse->bt = furi_record_open(RECORD_BT); + bt_mouse->notifications = furi_record_open(RECORD_NOTIFICATION); + bt_set_status_changed_callback( + bt_mouse->bt, bt_mouse_connection_status_changed_callback, bt_mouse); + furi_assert(bt_set_profile(bt_mouse->bt, BtProfileHidKeyboard)); + furi_hal_bt_start_advertising(); +} + +bool bt_mouse_custom_callback(uint32_t event, void* context) { + UNUSED(event); + furi_assert(context); + BtMouse* bt_mouse = context; + + tracking_step(bt_mouse_move, context); + furi_delay_ms(3); // Magic! Removing this will break the buttons + + view_dispatcher_send_custom_event(bt_mouse->view_dispatcher, 0); + return true; +} + +void bt_mouse_exit_callback(void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + + tracking_end(); + notification_internal_message(bt_mouse->notifications, &sequence_reset_blue); + + furi_hal_bt_stop_advertising(); + bt_set_profile(bt_mouse->bt, BtProfileSerial); + + furi_record_close(RECORD_NOTIFICATION); + bt_mouse->notifications = NULL; + furi_record_close(RECORD_BT); + bt_mouse->bt = NULL; +} + +static int8_t clamp(int t) { + if(t < -128) { + return -128; + } else if(t > 127) { + return 127; + } + return t; +} + +static int32_t bt_mouse_thread_callback(void* context) { + furi_assert(context); + BtMouse* bt_mouse = (BtMouse*)context; + + while(1) { + uint32_t flags = + furi_thread_flags_wait(BT_MOUSE_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); + if(flags & BT_MOUSE_FLAG_KILL_THREAD) { + break; + } + if(flags & BT_MOUSE_FLAG_INPUT_EVENT) { + furi_mutex_acquire(bt_mouse->mutex, FuriWaitForever); + + ButtonEvent event; + bool send_buttons = false; + if(bt_mouse->qhead != bt_mouse->qtail) { + event = bt_mouse->queue[bt_mouse->qhead++]; + bt_mouse->qhead %= BTN_EVT_QUEUE_SIZE; + send_buttons = true; + } + + int8_t dx = clamp(bt_mouse->dx); + bt_mouse->dx -= dx; + int8_t dy = clamp(bt_mouse->dy); + bt_mouse->dy -= dy; + int8_t wheel = clamp(bt_mouse->wheel); + bt_mouse->wheel -= wheel; + + furi_mutex_release(bt_mouse->mutex); + + if(bt_mouse->connected && send_buttons) { + if(event.state) { + furi_hal_bt_hid_mouse_press(event.button); + } else { + furi_hal_bt_hid_mouse_release(event.button); + } + } + + if(bt_mouse->connected && (dx != 0 || dy != 0)) { + furi_hal_bt_hid_mouse_move(dx, dy); + } + + if(bt_mouse->connected && wheel != 0) { + furi_hal_bt_hid_mouse_scroll(wheel); + } + } + } + + return 0; +} + +void bt_mouse_thread_start(BtMouse* bt_mouse) { + furi_assert(bt_mouse); + bt_mouse->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + bt_mouse->thread = furi_thread_alloc(); + furi_thread_set_name(bt_mouse->thread, "BtSender"); + furi_thread_set_stack_size(bt_mouse->thread, 1024); + furi_thread_set_context(bt_mouse->thread, bt_mouse); + furi_thread_set_callback(bt_mouse->thread, bt_mouse_thread_callback); + furi_thread_start(bt_mouse->thread); +} + +void bt_mouse_thread_stop(BtMouse* bt_mouse) { + furi_assert(bt_mouse); + FuriThreadId thread_id = furi_thread_get_id(bt_mouse->thread); + furi_assert(thread_id); + furi_thread_flags_set(thread_id, BT_MOUSE_FLAG_KILL_THREAD); + furi_thread_join(bt_mouse->thread); + furi_thread_free(bt_mouse->thread); + furi_mutex_free(bt_mouse->mutex); +} + +BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher) { + BtMouse* bt_mouse = malloc(sizeof(BtMouse)); + memset(bt_mouse, 0, sizeof(BtMouse)); + + bt_mouse->view = view_alloc(); + bt_mouse->view_dispatcher = view_dispatcher; + view_set_context(bt_mouse->view, bt_mouse); + view_set_draw_callback(bt_mouse->view, bt_mouse_draw_callback); + view_set_input_callback(bt_mouse->view, bt_mouse_input_callback); + view_set_enter_callback(bt_mouse->view, bt_mouse_enter_callback); + view_set_custom_callback(bt_mouse->view, bt_mouse_custom_callback); + view_set_exit_callback(bt_mouse->view, bt_mouse_exit_callback); + bt_mouse_thread_start(bt_mouse); + return bt_mouse; +} + +void bt_mouse_free(BtMouse* bt_mouse) { + furi_assert(bt_mouse); + bt_mouse_thread_stop(bt_mouse); + view_free(bt_mouse->view); + free(bt_mouse); +} + +View* bt_mouse_get_view(BtMouse* bt_mouse) { + furi_assert(bt_mouse); + return bt_mouse->view; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/bt_mouse.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/bt_mouse.h new file mode 100644 index 000000000..09153d8fa --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/bt_mouse.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +typedef struct BtMouse BtMouse; + +BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher); + +void bt_mouse_free(BtMouse* bt_mouse); + +View* bt_mouse_get_view(BtMouse* bt_mouse); + +void bt_mouse_set_connected_status(BtMouse* bt_mouse, bool connected); diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/calibration.c b/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/calibration.c new file mode 100644 index 000000000..a92f68be4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/calibration.c @@ -0,0 +1,69 @@ +#include "calibration.h" +#include "../tracking/main_loop.h" +#include "../air_mouse.h" + +#include +#include + +struct Calibration { + View* view; + ViewDispatcher* view_dispatcher; +}; + +static void calibration_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "Calibrating..."); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 63, "Please wait"); +} + +void calibration_enter_callback(void* context) { + furi_assert(context); + Calibration* calibration = context; + calibration_begin(); + view_dispatcher_send_custom_event(calibration->view_dispatcher, 0); +} + +bool calibration_custom_callback(uint32_t event, void* context) { + UNUSED(event); + furi_assert(context); + Calibration* calibration = context; + + if(calibration_step()) { + view_dispatcher_switch_to_view(calibration->view_dispatcher, AirMouseViewSubmenu); + } else { + view_dispatcher_send_custom_event(calibration->view_dispatcher, 0); + } + + return true; +} + +void calibration_exit_callback(void* context) { + furi_assert(context); + calibration_end(); +} + +Calibration* calibration_alloc(ViewDispatcher* view_dispatcher) { + Calibration* calibration = malloc(sizeof(Calibration)); + calibration->view = view_alloc(); + calibration->view_dispatcher = view_dispatcher; + view_set_context(calibration->view, calibration); + view_set_draw_callback(calibration->view, calibration_draw_callback); + view_set_enter_callback(calibration->view, calibration_enter_callback); + view_set_custom_callback(calibration->view, calibration_custom_callback); + view_set_exit_callback(calibration->view, calibration_exit_callback); + return calibration; +} + +void calibration_free(Calibration* calibration) { + furi_assert(calibration); + view_free(calibration->view); + free(calibration); +} + +View* calibration_get_view(Calibration* calibration) { + furi_assert(calibration); + return calibration->view; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/calibration.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/calibration.h new file mode 100644 index 000000000..da44ce0cd --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/calibration.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +typedef struct Calibration Calibration; + +Calibration* calibration_alloc(ViewDispatcher* view_dispatcher); + +void calibration_free(Calibration* calibration); + +View* calibration_get_view(Calibration* calibration); diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/usb_mouse.c b/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/usb_mouse.c new file mode 100644 index 000000000..09075b566 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/usb_mouse.c @@ -0,0 +1,139 @@ +#include "usb_mouse.h" +#include "../tracking/main_loop.h" + +#include +#include +#include +#include + +struct UsbMouse { + View* view; + ViewDispatcher* view_dispatcher; + FuriHalUsbInterface* usb_mode_prev; +}; + +static void usb_mouse_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "USB Mouse mode"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); +} + +#define MOUSE_SCROLL 2 + +static void usb_mouse_process(UsbMouse* usb_mouse, InputEvent* event) { + with_view_model( + usb_mouse->view, + void* model, + { + UNUSED(model); + if(event->key == InputKeyUp) { + if(event->type == InputTypePress) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT); + } else if(event->type == InputTypeRelease) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); + } + } else if(event->key == InputKeyDown) { + if(event->type == InputTypePress) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_RIGHT); + } else if(event->type == InputTypeRelease) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); + } + } else if(event->key == InputKeyOk) { + if(event->type == InputTypePress) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_WHEEL); + } else if(event->type == InputTypeRelease) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_WHEEL); + } + } else if(event->key == InputKeyRight) { + if(event->type == InputTypePress || event->type == InputTypeRepeat) { + furi_hal_hid_mouse_scroll(MOUSE_SCROLL); + } + } else if(event->key == InputKeyLeft) { + if(event->type == InputTypePress || event->type == InputTypeRepeat) { + furi_hal_hid_mouse_scroll(-MOUSE_SCROLL); + } + } + }, + true); +} + +static bool usb_mouse_input_callback(InputEvent* event, void* context) { + furi_assert(context); + UsbMouse* usb_mouse = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + // furi_hal_hid_mouse_release_all(); + } else { + usb_mouse_process(usb_mouse, event); + consumed = true; + } + + return consumed; +} + +void usb_mouse_enter_callback(void* context) { + furi_assert(context); + UsbMouse* usb_mouse = context; + + usb_mouse->usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); + + tracking_begin(); + + view_dispatcher_send_custom_event(usb_mouse->view_dispatcher, 0); +} + +bool usb_mouse_move(int8_t dx, int8_t dy, void* context) { + UNUSED(context); + return furi_hal_hid_mouse_move(dx, dy); +} + +bool usb_mouse_custom_callback(uint32_t event, void* context) { + UNUSED(event); + furi_assert(context); + UsbMouse* usb_mouse = context; + + tracking_step(usb_mouse_move, context); + furi_delay_ms(3); // Magic! Removing this will break the buttons + + view_dispatcher_send_custom_event(usb_mouse->view_dispatcher, 0); + return true; +} + +void usb_mouse_exit_callback(void* context) { + furi_assert(context); + UsbMouse* usb_mouse = context; + + tracking_end(); + + furi_hal_usb_set_config(usb_mouse->usb_mode_prev, NULL); +} + +UsbMouse* usb_mouse_alloc(ViewDispatcher* view_dispatcher) { + UsbMouse* usb_mouse = malloc(sizeof(UsbMouse)); + usb_mouse->view = view_alloc(); + usb_mouse->view_dispatcher = view_dispatcher; + view_set_context(usb_mouse->view, usb_mouse); + view_set_draw_callback(usb_mouse->view, usb_mouse_draw_callback); + view_set_input_callback(usb_mouse->view, usb_mouse_input_callback); + view_set_enter_callback(usb_mouse->view, usb_mouse_enter_callback); + view_set_custom_callback(usb_mouse->view, usb_mouse_custom_callback); + view_set_exit_callback(usb_mouse->view, usb_mouse_exit_callback); + return usb_mouse; +} + +void usb_mouse_free(UsbMouse* usb_mouse) { + furi_assert(usb_mouse); + view_free(usb_mouse->view); + free(usb_mouse); +} + +View* usb_mouse_get_view(UsbMouse* usb_mouse) { + furi_assert(usb_mouse); + return usb_mouse->view; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/usb_mouse.h b/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/usb_mouse.h new file mode 100644 index 000000000..5ce589a69 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/airmouse/views/usb_mouse.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +typedef struct UsbMouse UsbMouse; + +UsbMouse* usb_mouse_alloc(ViewDispatcher* view_dispatcher); + +void usb_mouse_free(UsbMouse* usb_mouse); + +View* usb_mouse_get_view(UsbMouse* usb_mouse); diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/LICENSE new file mode 100644 index 000000000..4c02d8221 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/LICENSE @@ -0,0 +1,22 @@ + +MIT License + +Copyright (c) 2023 Alan Tsui + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/README.md b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/README.md new file mode 100644 index 000000000..856f47344 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/README.md @@ -0,0 +1,71 @@ +

+

Barcode Generator

+

+ +A barcode generator for the Flipper Zero that supports **UPC-A**, **EAN-8**, **EAN-13**, **Code-39**, and **Code-128**[1] +

+ + +## Table of Contents +- [Table of Contents](#table-of-contents) +- [Installing](#installing) +- [Usage](#usage) + - [Creating a barcode](#creating-a-barcode) + - [Editing a barcode](#editing-a-barcode) + - [Deleting a barcode](#deleting-a-barcode) + - [Viewing a barcode](#viewing-a-barcode) +- [Screenshots](#screenshots) +- [Credits](#credits) + + +## Installing +1) Download the `.zip` file from the release section +2) Extract/unzip the `.zip` file onto your computer +3) Open qFlipper and go to the file manager +4) Navigate to the `apps` folder +5) Drag & Drop the `.fap` file into the `apps` folder +6) Navigate back to the root folder and create the folder `app_data`, if not already there +7) Navigate into `app_data` and create another folder called `barcode_data` +8) Navigate into `barcode_data` +9) Drag & Drop the encoding txts (`code39_encodings.txt` & `code128_encodings.txt`) into the `barcode_data` folder + + +## Usage + +### Creating a barcode +1) To create a barcode click on `Create Barcode` +2) Next select your type using the left and right arrows +3) Enter your filename and then your barcode data +4) Click save + +### Editing a barcode +1) To edit a barcode click on `Edit Barcode` +2) Next select the barcode file you want to edit +3) Edit the type, name, or data +4) Click save + +### Deleting a barcode +1) To delete a barcode click on `Edit Barcode` +2) Next select the barcode file you want to delete +3) Scroll all the way to the bottom +4) Click delete + +### Viewing a barcode +1) To view a barcode click on `Load Barcode` +2) Next select the barcode file you want to view + +## Screenshots +![Barcode Create Screen](screenshots/Creating%20Barcode.png "Barcode Create Screen") + +![Flipper Code-128 Barcode](screenshots/Flipper%20Barcode.png "Flipper Code-128 Barcode") + +![Flipper Box EAN-13 Barcode](screenshots/Flipper%20Box%20Barcode.png "Flipper Box EAN-13 Barcode") + +## Credits + +[Kingal1337](https://github.com/Kingal1337) - Developer + +[@teeebor](https://github.com/teeebor) - Menu Code Snippet + + +[1] - Only supports Set B and only the characters from 0-94 \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/application.fam b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/application.fam new file mode 100644 index 000000000..060900fea --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/application.fam @@ -0,0 +1,11 @@ +App( + appid="barcode_app", + name="Barcode", + apptype=FlipperAppType.EXTERNAL, + entry_point="barcode_main", + requires=["gui", "storage"], + stack_size=2 * 1024, + fap_category="Misc_Extra", + fap_icon="images/barcode_10.png", + fap_icon_assets="images", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_app.c b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_app.c new file mode 100644 index 000000000..45f10fb57 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_app.c @@ -0,0 +1,342 @@ +#include "barcode_app.h" + +#include "barcode_app_icons.h" + +/** + * Opens a file browser dialog and returns the filepath of the selected file + * + * @param folder the folder to view when the browser opens + * @param file_path a string pointer for the file_path when a file is selected, + * file_path will be the folder path is nothing is selected + * @returns true if a file is selected +*/ +static bool select_file(const char* folder, FuriString* file_path) { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, BARCODE_EXTENSION, &I_barcode_10); + browser_options.base_path = DEFAULT_USER_BARCODES; + furi_string_set(file_path, folder); + + bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options); + + furi_record_close(RECORD_DIALOGS); + + return res; +} + +/** + * Reads the data from a file and stores them in the FuriStrings raw_type and raw_data +*/ +ErrorCode read_raw_data(FuriString* file_path, FuriString* raw_type, FuriString* raw_data) { + //Open Storage + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_file_alloc(storage); + + ErrorCode reason = OKCode; + + if(!flipper_format_file_open_existing(ff, furi_string_get_cstr(file_path))) { + FURI_LOG_E(TAG, "Could not open file %s", furi_string_get_cstr(file_path)); + reason = FileOpening; + } else { + if(!flipper_format_read_string(ff, "Type", raw_type)) { + FURI_LOG_E(TAG, "Could not read \"Type\" string"); + reason = InvalidFileData; + } + if(!flipper_format_read_string(ff, "Data", raw_data)) { + FURI_LOG_E(TAG, "Could not read \"Data\" string"); + reason = InvalidFileData; + } + } + + //Close Storage + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + return reason; +} + +/** + * Gets the file name from a file path + * @param file_path the file path + * @param file_name the FuriString to store the file name + * @param remove_extension true if the extension should be removed, otherwise false +*/ +bool get_file_name_from_path(FuriString* file_path, FuriString* file_name, bool remove_extension) { + if(file_path == NULL || file_name == NULL) { + return false; + } + int slash_index = furi_string_search_rchar(file_path, '/', 0); + if(slash_index == FURI_STRING_FAILURE || slash_index >= (furi_string_size(file_path) - 1)) { + return false; + } + + furi_string_set(file_name, file_path); + furi_string_right(file_name, slash_index + 1); + if(remove_extension) { + int ext_index = furi_string_search_rchar(file_name, '.', 0); + if(ext_index != FURI_STRING_FAILURE && ext_index < (furi_string_size(file_path))) { + furi_string_left(file_name, ext_index); + } + } + + return true; +} + +/** + * Creates the barcode folder +*/ +void init_folder() { + Storage* storage = furi_record_open(RECORD_STORAGE); + FURI_LOG_I(TAG, "Creating barcodes folder"); + if(storage_simply_mkdir(storage, DEFAULT_USER_BARCODES)) { + FURI_LOG_I(TAG, "Barcodes folder successfully created!"); + } else { + FURI_LOG_I(TAG, "Barcodes folder already exists."); + } + furi_record_close(RECORD_STORAGE); +} + +void select_barcode_item(BarcodeApp* app) { + FuriString* file_path = furi_string_alloc(); + FuriString* raw_type = furi_string_alloc(); + FuriString* raw_data = furi_string_alloc(); + + //this determines if the data was read correctly or if the + bool loaded_success = true; + ErrorCode reason = OKCode; + + bool file_selected = select_file(DEFAULT_USER_BARCODES, file_path); + if(file_selected) { + FURI_LOG_I(TAG, "The file selected is %s", furi_string_get_cstr(file_path)); + Barcode* barcode = app->barcode_view; + + reason = read_raw_data(file_path, raw_type, raw_data); + if(reason != OKCode) { + loaded_success = false; + FURI_LOG_E(TAG, "Could not read data correctly"); + } + + //Free the data from the previous barcode + barcode_free_model(barcode); + + with_view_model( + barcode->view, + BarcodeModel * model, + { + model->file_path = furi_string_alloc_set(file_path); + + model->data = malloc(sizeof(BarcodeData)); + model->data->valid = loaded_success; + + if(loaded_success) { + model->data->raw_data = furi_string_alloc_set(raw_data); + model->data->correct_data = furi_string_alloc(); + + model->data->type_obj = get_type(raw_type); + + barcode_loader(model->data); + } else { + model->data->reason = reason; + } + }, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, BarcodeView); + } + + furi_string_free(raw_type); + furi_string_free(raw_data); + furi_string_free(file_path); +} + +void edit_barcode_item(BarcodeApp* app) { + FuriString* file_path = furi_string_alloc(); + FuriString* file_name = furi_string_alloc(); + FuriString* raw_type = furi_string_alloc(); + FuriString* raw_data = furi_string_alloc(); + + //this determines if the data was read correctly or if the + ErrorCode reason = OKCode; + + bool file_selected = select_file(DEFAULT_USER_BARCODES, file_path); + if(file_selected) { + FURI_LOG_I(TAG, "The file selected is %s", furi_string_get_cstr(file_path)); + CreateView* create_view_object = app->create_view; + + reason = read_raw_data(file_path, raw_type, raw_data); + if(reason != OKCode) { + FURI_LOG_E(TAG, "Could not read data correctly"); + with_view_model( + app->message_view->view, + MessageViewModel * model, + { model->message = get_error_code_message(reason); }, + true); + + view_dispatcher_switch_to_view( + create_view_object->barcode_app->view_dispatcher, MessageErrorView); + + } else { + BarcodeTypeObj* type_obj = get_type(raw_type); + if(type_obj->type == UNKNOWN) { + type_obj = barcode_type_objs[0]; + } + get_file_name_from_path(file_path, file_name, true); + + create_view_free_model(create_view_object); + with_view_model( + create_view_object->view, + CreateViewModel * model, + { + model->selected_menu_item = 0; + model->barcode_type = type_obj; + model->file_path = furi_string_alloc_set(file_path); + model->file_name = furi_string_alloc_set(file_name); + model->barcode_data = furi_string_alloc_set(raw_data); + model->mode = EditMode; + }, + true); + view_dispatcher_switch_to_view(app->view_dispatcher, CreateBarcodeView); + } + } + + furi_string_free(raw_type); + furi_string_free(raw_data); + furi_string_free(file_name); + furi_string_free(file_path); +} + +void create_barcode_item(BarcodeApp* app) { + CreateView* create_view_object = app->create_view; + + create_view_free_model(create_view_object); + + with_view_model( + create_view_object->view, + CreateViewModel * model, + { + model->selected_menu_item = 0; + model->barcode_type = barcode_type_objs[0]; + model->file_path = furi_string_alloc(); + model->file_name = furi_string_alloc(); + model->barcode_data = furi_string_alloc(); + model->mode = NewMode; + }, + true); + view_dispatcher_switch_to_view(app->view_dispatcher, CreateBarcodeView); +} + +void submenu_callback(void* context, uint32_t index) { + furi_assert(context); + + BarcodeApp* app = context; + + if(index == SelectBarcodeItem) { + select_barcode_item(app); + } else if(index == EditBarcodeItem) { + edit_barcode_item(app); + } else if(index == CreateBarcodeItem) { + create_barcode_item(app); + } +} + +uint32_t main_menu_callback(void* context) { + UNUSED(context); + return MainMenuView; +} + +uint32_t exit_callback(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +void free_app(BarcodeApp* app) { + FURI_LOG_I(TAG, "Freeing Data"); + + init_folder(); + free_types(); + + view_dispatcher_remove_view(app->view_dispatcher, TextInputView); + text_input_free(app->text_input); + + view_dispatcher_remove_view(app->view_dispatcher, MessageErrorView); + message_view_free(app->message_view); + + view_dispatcher_remove_view(app->view_dispatcher, MainMenuView); + submenu_free(app->main_menu); + + view_dispatcher_remove_view(app->view_dispatcher, CreateBarcodeView); + create_view_free(app->create_view); + + view_dispatcher_remove_view(app->view_dispatcher, BarcodeView); + barcode_free(app->barcode_view); + + //free the dispatcher + view_dispatcher_free(app->view_dispatcher); + + furi_message_queue_free(app->event_queue); + + furi_record_close(RECORD_GUI); + app->gui = NULL; + + free(app); +} + +int32_t barcode_main(void* p) { + UNUSED(p); + BarcodeApp* app = malloc(sizeof(BarcodeApp)); + init_types(); + app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + // Register view port in GUI + app->gui = furi_record_open(RECORD_GUI); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->main_menu = submenu_alloc(); + submenu_add_item(app->main_menu, "Load Barcode", SelectBarcodeItem, submenu_callback, app); + view_set_previous_callback(submenu_get_view(app->main_menu), exit_callback); + view_dispatcher_add_view(app->view_dispatcher, MainMenuView, submenu_get_view(app->main_menu)); + + submenu_add_item(app->main_menu, "Edit Barcode", EditBarcodeItem, submenu_callback, app); + + /***************************** + * Creating Text Input View + ******************************/ + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, TextInputView, text_input_get_view(app->text_input)); + + /***************************** + * Creating Message View + ******************************/ + app->message_view = message_view_allocate(app); + view_dispatcher_add_view( + app->view_dispatcher, MessageErrorView, message_get_view(app->message_view)); + + /***************************** + * Creating Create View + ******************************/ + app->create_view = create_view_allocate(app); + submenu_add_item(app->main_menu, "Create Barcode", CreateBarcodeItem, submenu_callback, app); + view_set_previous_callback(create_get_view(app->create_view), main_menu_callback); + view_dispatcher_add_view( + app->view_dispatcher, CreateBarcodeView, create_get_view(app->create_view)); + + /***************************** + * Creating Barcode View + ******************************/ + app->barcode_view = barcode_view_allocate(app); + view_set_previous_callback(barcode_get_view(app->barcode_view), main_menu_callback); + view_dispatcher_add_view( + app->view_dispatcher, BarcodeView, barcode_get_view(app->barcode_view)); + + //switch view to submenu and run dispatcher + view_dispatcher_switch_to_view(app->view_dispatcher, MainMenuView); + view_dispatcher_run(app->view_dispatcher); + + free_app(app); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_app.h b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_app.h new file mode 100644 index 000000000..31c805a69 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_app.h @@ -0,0 +1,87 @@ +#pragma once +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "barcode_utils.h" + +#define TAG "BARCODE" +#define VERSION "1.0" +#define FILE_VERSION "1" + +#define TEXT_BUFFER_SIZE 128 + +#define BARCODE_HEIGHT 50 +#define BARCODE_Y_START 3 + +#define APPS_DATA EXT_PATH("apps_data") + +//the folder where the encodings are located +#define BARCODE_DATA_FILE_DIR_PATH APPS_DATA "/barcode_data" + +//the folder where the code 39 encoding table is located +#define CODE39_DICT_FILE_PATH BARCODE_DATA_FILE_DIR_PATH "/code39_encodings.txt" + +//the folder where the code 128 encoding table is located +#define CODE128_DICT_FILE_PATH BARCODE_DATA_FILE_DIR_PATH "/code128_encodings.txt" + +//the folder where the user stores their barcodes +#define DEFAULT_USER_BARCODES EXT_PATH("barcodes") + +//The extension barcode files use +#define BARCODE_EXTENSION ".barcode" +#define BARCODE_EXTENSION_LENGTH 8 + +#include "views/barcode_view.h" +#include "views/create_view.h" +#include "views/message_view.h" +#include "barcode_validator.h" + +typedef struct BarcodeApp BarcodeApp; + +struct BarcodeApp { + Submenu* main_menu; + ViewDispatcher* view_dispatcher; + Gui* gui; + + FuriMessageQueue* event_queue; + + CreateView* create_view; + Barcode* barcode_view; + + MessageView* message_view; + TextInput* text_input; +}; + +enum SubmenuItems { + SelectBarcodeItem, + EditBarcodeItem, + + CreateBarcodeItem +}; + +enum Views { + TextInputView, + MessageErrorView, + MainMenuView, + CreateBarcodeView, + + BarcodeView +}; + +void submenu_callback(void* context, uint32_t index); + +uint32_t main_menu_callback(void* context); + +uint32_t exit_callback(void* context); + +int32_t barcode_main(void* p); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_utils.c b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_utils.c new file mode 100644 index 000000000..0a4770045 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_utils.c @@ -0,0 +1,125 @@ +#include "barcode_utils.h" + +BarcodeTypeObj* barcode_type_objs[NUMBER_OF_BARCODE_TYPES] = {NULL}; + +void init_types() { + BarcodeTypeObj* upc_a = malloc(sizeof(BarcodeTypeObj)); + upc_a->name = "UPC-A"; + upc_a->type = UPCA; + upc_a->min_digits = 11; + upc_a->max_digits = 12; + upc_a->start_pos = 16; + barcode_type_objs[UPCA] = upc_a; + + BarcodeTypeObj* ean_8 = malloc(sizeof(BarcodeTypeObj)); + ean_8->name = "EAN-8"; + ean_8->type = EAN8; + ean_8->min_digits = 7; + ean_8->max_digits = 8; + ean_8->start_pos = 32; + barcode_type_objs[EAN8] = ean_8; + + BarcodeTypeObj* ean_13 = malloc(sizeof(BarcodeTypeObj)); + ean_13->name = "EAN-13"; + ean_13->type = EAN13; + ean_13->min_digits = 12; + ean_13->max_digits = 13; + ean_13->start_pos = 16; + barcode_type_objs[EAN13] = ean_13; + + BarcodeTypeObj* code_39 = malloc(sizeof(BarcodeTypeObj)); + code_39->name = "CODE-39"; + code_39->type = CODE39; + code_39->min_digits = 1; + code_39->max_digits = -1; + code_39->start_pos = 0; + barcode_type_objs[CODE39] = code_39; + + BarcodeTypeObj* code_128 = malloc(sizeof(BarcodeTypeObj)); + code_128->name = "CODE-128"; + code_128->type = CODE128; + code_128->min_digits = 1; + code_128->max_digits = -1; + code_128->start_pos = 0; + barcode_type_objs[CODE128] = code_128; + + BarcodeTypeObj* unknown = malloc(sizeof(BarcodeTypeObj)); + unknown->name = "Unknown"; + unknown->type = UNKNOWN; + unknown->min_digits = 0; + unknown->max_digits = 0; + unknown->start_pos = 0; + barcode_type_objs[UNKNOWN] = unknown; +} + +void free_types() { + for(int i = 0; i < NUMBER_OF_BARCODE_TYPES; i++) { + free(barcode_type_objs[i]); + } +} + +BarcodeTypeObj* get_type(FuriString* type_string) { + if(furi_string_cmp_str(type_string, "UPC-A") == 0) { + return barcode_type_objs[UPCA]; + } + if(furi_string_cmp_str(type_string, "EAN-8") == 0) { + return barcode_type_objs[EAN8]; + } + if(furi_string_cmp_str(type_string, "EAN-13") == 0) { + return barcode_type_objs[EAN13]; + } + if(furi_string_cmp_str(type_string, "CODE-39") == 0) { + return barcode_type_objs[CODE39]; + } + if(furi_string_cmp_str(type_string, "CODE-128") == 0) { + return barcode_type_objs[CODE128]; + } + + return barcode_type_objs[UNKNOWN]; +} + +const char* get_error_code_name(ErrorCode error_code) { + switch(error_code) { + case WrongNumberOfDigits: + return "Wrong Number Of Digits"; + case InvalidCharacters: + return "Invalid Characters"; + case UnsupportedType: + return "Unsupported Type"; + case FileOpening: + return "File Opening Error"; + case InvalidFileData: + return "Invalid File Data"; + case MissingEncodingTable: + return "Missing Encoding Table"; + case EncodingTableError: + return "Encoding Table Error"; + case OKCode: + return "OK"; + default: + return "Unknown Code"; + }; +} + +const char* get_error_code_message(ErrorCode error_code) { + switch(error_code) { + case WrongNumberOfDigits: + return "Wrong # of characters"; + case InvalidCharacters: + return "Invalid characters"; + case UnsupportedType: + return "Unsupported barcode type"; + case FileOpening: + return "Could not open file"; + case InvalidFileData: + return "Invalid file data"; + case MissingEncodingTable: + return "Missing encoding table"; + case EncodingTableError: + return "Encoding table error"; + case OKCode: + return "OK"; + default: + return "Could not read barcode data"; + }; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_utils.h b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_utils.h new file mode 100644 index 000000000..212923a89 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_utils.h @@ -0,0 +1,53 @@ + +#pragma once +#include +#include + +#define NUMBER_OF_BARCODE_TYPES 6 + +typedef enum { + WrongNumberOfDigits, //There is too many or too few digits in the barcode + InvalidCharacters, //The barcode contains invalid characters + UnsupportedType, //the barcode type is not supported + FileOpening, //A problem occurred when opening the barcode data file + InvalidFileData, //One of the key in the file doesn't exist or there is a typo + MissingEncodingTable, //The encoding table txt for the barcode type is missing + EncodingTableError, //Something is wrong with the encoding table, probably missing data or typo + OKCode +} ErrorCode; + +typedef enum { + UPCA, + EAN8, + EAN13, + CODE39, + CODE128, + + UNKNOWN +} BarcodeType; + +typedef struct { + char* name; //The name of the barcode type + BarcodeType type; //The barcode type enum + int min_digits; //the minimum number of digits + int max_digits; //the maximum number of digits + int start_pos; //where to start drawing the barcode, set to -1 to dynamically draw barcode +} BarcodeTypeObj; + +typedef struct { + BarcodeTypeObj* type_obj; + int check_digit; //A place to store the check digit + FuriString* raw_data; //the data directly from the file + FuriString* correct_data; //the corrected/processed data + bool valid; //true if the raw data is correctly formatted, such as correct num of digits, valid characters, etc. + ErrorCode reason; //the reason why this barcode is invalid +} BarcodeData; + +//All available barcode types +extern BarcodeTypeObj* barcode_type_objs[NUMBER_OF_BARCODE_TYPES]; + +void init_types(); +void free_types(); +BarcodeTypeObj* get_type(FuriString* type_string); +const char* get_error_code_name(ErrorCode error_code); +const char* get_error_code_message(ErrorCode error_code); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_validator.c b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_validator.c new file mode 100644 index 000000000..abb1d9fba --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_validator.c @@ -0,0 +1,344 @@ +#include "barcode_validator.h" + +void barcode_loader(BarcodeData* barcode_data) { + switch(barcode_data->type_obj->type) { + case UPCA: + case EAN8: + case EAN13: + ean_upc_loader(barcode_data); + break; + case CODE39: + code_39_loader(barcode_data); + break; + case CODE128: + code_128_loader(barcode_data); + break; + case UNKNOWN: + barcode_data->reason = UnsupportedType; + barcode_data->valid = false; + default: + break; + } +} + +/** + * Calculates the check digit of a barcode if they have one + * @param barcode_data the barcode data + * @returns a check digit or -1 for either an invalid +*/ +int calculate_check_digit(BarcodeData* barcode_data) { + int check_digit = -1; + switch(barcode_data->type_obj->type) { + case UPCA: + case EAN8: + case EAN13: + check_digit = calculate_ean_upc_check_digit(barcode_data); + break; + case CODE39: + case CODE128: + case UNKNOWN: + default: + break; + } + + return check_digit; +} + +/** + * Calculates the check digit of barcode types UPC-A, EAN-8, & EAN-13 +*/ +int calculate_ean_upc_check_digit(BarcodeData* barcode_data) { + int check_digit = 0; + int odd = 0; + int even = 0; + + int length = barcode_data->type_obj->min_digits; + + //Get sum of odd digits + for(int i = 0; i < length; i += 2) { + odd += furi_string_get_char(barcode_data->raw_data, i) - '0'; + } + + //Get sum of even digits + for(int i = 1; i < length; i += 2) { + even += furi_string_get_char(barcode_data->raw_data, i) - '0'; + } + + if(barcode_data->type_obj->type == EAN13) { + check_digit = even * 3 + odd; + } else { + check_digit = odd * 3 + even; + } + + check_digit = check_digit % 10; + + return (10 - check_digit) % 10; +} + +/** + * Loads and validates Barcode Types EAN-8, EAN-13, and UPC-A + * barcode_data and its strings should already be allocated; +*/ +void ean_upc_loader(BarcodeData* barcode_data) { + int barcode_length = furi_string_size(barcode_data->raw_data); + + int min_digits = barcode_data->type_obj->min_digits; + int max_digit = barcode_data->type_obj->max_digits; + + //check the length of the barcode + if(barcode_length < min_digits || barcode_length > max_digit) { + barcode_data->reason = WrongNumberOfDigits; + barcode_data->valid = false; + return; + } + + //checks if the barcode contains any characters that aren't a number + for(int i = 0; i < barcode_length; i++) { + char character = furi_string_get_char(barcode_data->raw_data, i); + int digit = character - '0'; //convert the number into an int (also the index) + if(digit < 0 || digit > 9) { + barcode_data->reason = InvalidCharacters; + barcode_data->valid = false; + return; + } + } + + int check_digit = calculate_check_digit(barcode_data); + char check_digit_char = check_digit + '0'; + + barcode_data->check_digit = check_digit; + + //if the barcode length is at max length then we will verify if the check digit is correct + if(barcode_length == max_digit) { + //append the raw_data to the correct data string + furi_string_cat(barcode_data->correct_data, barcode_data->raw_data); + + //append the check digit to the correct data string + furi_string_set_char(barcode_data->correct_data, min_digits, check_digit_char); + } + //if the barcode length is at min length, we will calculate the check digit + if(barcode_length == min_digits) { + //append the raw_data to the correct data string + furi_string_cat(barcode_data->correct_data, barcode_data->raw_data); + + //append the check digit to the correct data string + furi_string_push_back(barcode_data->correct_data, check_digit_char); + } +} + +void code_39_loader(BarcodeData* barcode_data) { + int barcode_length = furi_string_size(barcode_data->raw_data); + + int min_digits = barcode_data->type_obj->min_digits; + int max_digit = barcode_data->type_obj->max_digits; + + //check the length of the barcode, must contain atleast a character, + //this can have as many characters as it wants, it might not fit on the screen + if(barcode_length < min_digits) { + barcode_data->reason = WrongNumberOfDigits; + barcode_data->valid = false; + return; + } + + FuriString* barcode_bits = furi_string_alloc(); + FuriString* temp_string = furi_string_alloc(); + + //add starting and ending * + if(!furi_string_start_with(barcode_data->raw_data, "*")) { + furi_string_push_back(temp_string, '*'); + furi_string_cat(temp_string, barcode_data->raw_data); + furi_string_set(barcode_data->raw_data, temp_string); + } + + if(!furi_string_end_with(barcode_data->raw_data, "*")) { + furi_string_push_back(barcode_data->raw_data, '*'); + } + + furi_string_free(temp_string); + barcode_length = furi_string_size(barcode_data->raw_data); + + //Open Storage + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_file_alloc(storage); + + if(!flipper_format_file_open_existing(ff, CODE39_DICT_FILE_PATH)) { + FURI_LOG_E(TAG, "Could not open file %s", CODE39_DICT_FILE_PATH); + barcode_data->reason = MissingEncodingTable; + barcode_data->valid = false; + } else { + FuriString* char_bits = furi_string_alloc(); + for(int i = 0; i < barcode_length; i++) { + char barcode_char = toupper(furi_string_get_char(barcode_data->raw_data, i)); + + //convert a char into a string so it used in flipper_format_read_string + char current_character[2]; + snprintf(current_character, 2, "%c", barcode_char); + + if(!flipper_format_read_string(ff, current_character, char_bits)) { + FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char); + barcode_data->reason = InvalidCharacters; + barcode_data->valid = false; + break; + } else { + FURI_LOG_I( + TAG, "\"%c\" string: %s", barcode_char, furi_string_get_cstr(char_bits)); + furi_string_cat(barcode_bits, char_bits); + } + flipper_format_rewind(ff); + } + furi_string_free(char_bits); + } + + //Close Storage + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + furi_string_cat(barcode_data->correct_data, barcode_bits); + furi_string_free(barcode_bits); +} + +/** + * Loads a code 128 barcode + * + * Only supports character set B +*/ +void code_128_loader(BarcodeData* barcode_data) { + int barcode_length = furi_string_size(barcode_data->raw_data); + + //the start code for character set B + int start_code_value = 104; + + //The bits for the start code + const char* start_code_bits = "11010010000"; + + //The bits for the stop code + const char* stop_code_bits = "1100011101011"; + + int min_digits = barcode_data->type_obj->min_digits; + int max_digit = barcode_data->type_obj->max_digits; + + /** + * A sum of all of the characters values + * Ex: + * Barcode Data : ABC + * A has a value of 33 + * B has a value of 34 + * C has a value of 35 + * + * the checksum_adder would be (33 * 1) + (34 * 2) + (35 * 3) + 104 = 310 + * + * Add 104 since we are using set B + */ + int checksum_adder = start_code_value; + /** + * Checksum digits is the number of characters it has read so far + * In the above example the checksum_digits would be 3 + */ + int checksum_digits = 0; + + //the calculated check digit + int final_check_digit = 0; + + //check the length of the barcode, must contain atleast a character, + //this can have as many characters as it wants, it might not fit on the screen + if(barcode_length < min_digits) { + barcode_data->reason = WrongNumberOfDigits; + barcode_data->valid = false; + return; + } + + //Open Storage + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_file_alloc(storage); + + FuriString* barcode_bits = furi_string_alloc(); + + //add the start code + furi_string_cat(barcode_bits, start_code_bits); + + if(!flipper_format_file_open_existing(ff, CODE128_DICT_FILE_PATH)) { + FURI_LOG_E(TAG, "Could not open file %s", CODE128_DICT_FILE_PATH); + barcode_data->reason = MissingEncodingTable; + barcode_data->valid = false; + } else { + FuriString* value = furi_string_alloc(); + FuriString* char_bits = furi_string_alloc(); + for(int i = 0; i < barcode_length; i++) { + char barcode_char = furi_string_get_char(barcode_data->raw_data, i); + + //convert a char into a string so it used in flipper_format_read_string + char current_character[2]; + snprintf(current_character, 2, "%c", barcode_char); + + //get the value of the character + if(!flipper_format_read_string(ff, current_character, value)) { + FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char); + barcode_data->reason = InvalidCharacters; + barcode_data->valid = false; + break; + } + //using the value of the character, get the characters bits + if(!flipper_format_read_string(ff, furi_string_get_cstr(value), char_bits)) { + FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char); + barcode_data->reason = EncodingTableError; + barcode_data->valid = false; + break; + } else { + //add the bits to the full barcode + furi_string_cat(barcode_bits, char_bits); + + //calculate the checksum + checksum_digits += 1; + checksum_adder += (atoi(furi_string_get_cstr(value)) * checksum_digits); + + FURI_LOG_D( + TAG, + "\"%c\" string: %s : %s : %d : %d : %d", + barcode_char, + furi_string_get_cstr(char_bits), + furi_string_get_cstr(value), + checksum_digits, + (atoi(furi_string_get_cstr(value)) * checksum_digits), + checksum_adder); + } + //bring the file pointer back to the beginning + flipper_format_rewind(ff); + } + + //calculate the check digit and convert it into a c string for lookup in the encoding table + final_check_digit = checksum_adder % 103; + int length = snprintf(NULL, 0, "%d", final_check_digit); + char* final_check_digit_string = malloc(length + 1); + snprintf(final_check_digit_string, length + 1, "%d", final_check_digit); + + //after the checksum has been calculated, add the bits to the full barcode + if(!flipper_format_read_string(ff, final_check_digit_string, char_bits)) { + FURI_LOG_E(TAG, "Could not read \"%s\" string", final_check_digit_string); + barcode_data->reason = EncodingTableError; + barcode_data->valid = false; + } else { + //add the check digit bits to the full barcode + furi_string_cat(barcode_bits, char_bits); + + FURI_LOG_D( + TAG, + "\"%s\" string: %s", + final_check_digit_string, + furi_string_get_cstr(char_bits)); + } + + free(final_check_digit_string); + furi_string_free(value); + furi_string_free(char_bits); + } + + //add the stop code + furi_string_cat(barcode_bits, stop_code_bits); + + //Close Storage + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + furi_string_cat(barcode_data->correct_data, barcode_bits); + furi_string_free(barcode_bits); +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_validator.h b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_validator.h new file mode 100644 index 000000000..962d14729 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/barcode_validator.h @@ -0,0 +1,13 @@ +#pragma once + +#include "barcode_app.h" + +int calculate_check_digit(BarcodeData* barcode_data); +int calculate_ean_upc_check_digit(BarcodeData* barcode_data); +void ean_upc_loader(BarcodeData* barcode_data); +void upc_a_loader(BarcodeData* barcode_data); +void ean_8_loader(BarcodeData* barcode_data); +void ean_13_loader(BarcodeData* barcode_data); +void code_39_loader(BarcodeData* barcode_data); +void code_128_loader(BarcodeData* barcode_data); +void barcode_loader(BarcodeData* barcode_data); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/encodings.c b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/encodings.c new file mode 100644 index 000000000..764fde796 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/encodings.c @@ -0,0 +1,52 @@ +#include "encodings.h" + +const char EAN_13_STRUCTURE_CODES[10][6] = { + "LLLLLL", + "LLGLGG", + "LLGGLG", + "LLGGGL", + "LGLLGG", + "LGGLLG", + "LGGGLL", + "LGLGLG", + "LGLGGL", + "LGGLGL"}; + +const char UPC_EAN_L_CODES[10][8] = { + "0001101", // 0 + "0011001", // 1 + "0010011", // 2 + "0111101", // 3 + "0100011", // 4 + "0110001", // 5 + "0101111", // 6 + "0111011", // 7 + "0110111", // 8 + "0001011" // 9 +}; + +const char EAN_G_CODES[10][8] = { + "0100111", // 0 + "0110011", // 1 + "0011011", // 2 + "0100001", // 3 + "0011101", // 4 + "0111001", // 5 + "0000101", // 6 + "0010001", // 7 + "0001001", // 8 + "0010111" // 9 +}; + +const char UPC_EAN_R_CODES[10][8] = { + "1110010", // 0 + "1100110", // 1 + "1101100", // 2 + "1000010", // 3 + "1011100", // 4 + "1001110", // 5 + "1010000", // 6 + "1000100", // 7 + "1001000", // 8 + "1110100" // 9 +}; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/encodings.h b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/encodings.h new file mode 100644 index 000000000..c5b8d61ff --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/encodings.h @@ -0,0 +1,6 @@ +#pragma once + +extern const char EAN_13_STRUCTURE_CODES[10][6]; +extern const char UPC_EAN_L_CODES[10][8]; +extern const char EAN_G_CODES[10][8]; +extern const char UPC_EAN_R_CODES[10][8]; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/images/barcode_10.png b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/images/barcode_10.png new file mode 100644 index 000000000..32d4971ad Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/images/barcode_10.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/screenshots/Creating Barcode.png b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/screenshots/Creating Barcode.png new file mode 100644 index 000000000..e976b5682 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/screenshots/Creating Barcode.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/screenshots/Flipper Barcode.png b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/screenshots/Flipper Barcode.png new file mode 100644 index 000000000..a6758c621 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/screenshots/Flipper Barcode.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/screenshots/Flipper Box Barcode.png b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/screenshots/Flipper Box Barcode.png new file mode 100644 index 000000000..8dbd7d2a9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/screenshots/Flipper Box Barcode.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/barcode_view.c b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/barcode_view.c new file mode 100644 index 000000000..afd727b63 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/barcode_view.c @@ -0,0 +1,444 @@ +#include "../barcode_app.h" +#include "barcode_view.h" +#include "../encodings.h" + +/** + * @brief Draws a single bit from a barcode at a specified location + * @param canvas + * @param bit a 1 or a 0 to signify a bit of data + * @param x the top left x coordinate + * @param y the top left y coordinate + * @param width the width of the bit + * @param height the height of the bit + */ +static void draw_bit(Canvas* canvas, int bit, int x, int y, int width, int height) { + if(bit == 1) { + canvas_set_color(canvas, ColorBlack); + } else { + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_box(canvas, x, y, width, height); +} + +/** + * +*/ +static void draw_error_str(Canvas* canvas, const char* error) { + canvas_clear(canvas); + canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error); +} + +/** + * @param bits a string of 1's and 0's + * @returns the x coordinate after the bits have been drawn, useful for drawing the next section of bits +*/ +static int draw_bits(Canvas* canvas, const char* bits, int x, int y, int width, int height) { + int bits_length = strlen(bits); + for(int i = 0; i < bits_length; i++) { + char c = bits[i]; + int num = c - '0'; + + draw_bit(canvas, num, x, y, width, height); + + x += width; + } + return x; +} + +/** + * Draws an EAN-8 type barcode, does not check if the barcode is valid + * @param canvas the canvas + * @param barcode_digits the digits in the barcode, must be 8 characters long +*/ +static void draw_ean_8(Canvas* canvas, BarcodeData* barcode_data) { + FuriString* barcode_digits = barcode_data->correct_data; + BarcodeTypeObj* type_obj = barcode_data->type_obj; + + int barcode_length = furi_string_size(barcode_digits); + + int x = type_obj->start_pos; + int y = BARCODE_Y_START; + int width = 1; + int height = BARCODE_HEIGHT; + + //the guard patterns for the beginning, center, ending + const char* end_bits = "101"; + const char* center_bits = "01010"; + + //draw the starting guard pattern + x = draw_bits(canvas, end_bits, x, y, width, height + 5); + + FuriString* code_part = furi_string_alloc(); + + //loop through each digit, find the encoding, and draw it + for(int i = 0; i < barcode_length; i++) { + char current_digit = furi_string_get_char(barcode_digits, i); + + //the actual number and the index of the bits + int index = current_digit - '0'; + //use the L-codes for the first 4 digits and the R-Codes for the last 4 digits + if(i <= 3) { + furi_string_set_str(code_part, UPC_EAN_L_CODES[index]); + } else { + furi_string_set_str(code_part, UPC_EAN_R_CODES[index]); + } + + //convert the current_digit char into a string so it can be printed + char current_digit_string[2]; + snprintf(current_digit_string, 2, "%c", current_digit); + + //set the canvas color to black to print the digit + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, x + 1, y + height + 8, current_digit_string); + + //draw the bits of the barcode + x = draw_bits(canvas, furi_string_get_cstr(code_part), x, y, width, height); + + //if the index has reached 3, that means 4 digits have been drawn and now draw the center guard pattern + if(i == 3) { + x = draw_bits(canvas, center_bits, x, y, width, height + 5); + } + } + furi_string_free(code_part); + + //draw the ending guard pattern + x = draw_bits(canvas, end_bits, x, y, width, height + 5); +} + +static void draw_ean_13(Canvas* canvas, BarcodeData* barcode_data) { + FuriString* barcode_digits = barcode_data->correct_data; + BarcodeTypeObj* type_obj = barcode_data->type_obj; + + int barcode_length = furi_string_size(barcode_digits); + + int x = type_obj->start_pos; + int y = BARCODE_Y_START; + int width = 1; + int height = BARCODE_HEIGHT; + + //the guard patterns for the beginning, center, ending + const char* end_bits = "101"; + const char* center_bits = "01010"; + + //draw the starting guard pattern + x = draw_bits(canvas, end_bits, x, y, width, height + 5); + + FuriString* left_structure = furi_string_alloc(); + FuriString* code_part = furi_string_alloc(); + + //loop through each digit, find the encoding, and draw it + for(int i = 0; i < barcode_length; i++) { + char current_digit = furi_string_get_char(barcode_digits, i); + int index = current_digit - '0'; + + if(i == 0) { + furi_string_set_str(left_structure, EAN_13_STRUCTURE_CODES[index]); + + //convert the current_digit char into a string so it can be printed + char current_digit_string[2]; + snprintf(current_digit_string, 2, "%c", current_digit); + + //set the canvas color to black to print the digit + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, x - 10, y + height + 8, current_digit_string); + + continue; + } else { + //use the L-codes for the first 6 digits and the R-Codes for the last 6 digits + if(i <= 6) { + //get the encoding type at the current barcode bit position + char encoding_type = furi_string_get_char(left_structure, i - 1); + if(encoding_type == 'L') { + furi_string_set_str(code_part, UPC_EAN_L_CODES[index]); + } else { + furi_string_set_str(code_part, EAN_G_CODES[index]); + } + } else { + furi_string_set_str(code_part, UPC_EAN_R_CODES[index]); + } + + //convert the current_digit char into a string so it can be printed + char current_digit_string[2]; + snprintf(current_digit_string, 2, "%c", current_digit); + + //set the canvas color to black to print the digit + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, x + 1, y + height + 8, current_digit_string); + + //draw the bits of the barcode + x = draw_bits(canvas, furi_string_get_cstr(code_part), x, y, width, height); + + //if the index has reached 6, that means 6 digits have been drawn and we now draw the center guard pattern + if(i == 6) { + x = draw_bits(canvas, center_bits, x, y, width, height + 5); + } + } + } + + furi_string_free(left_structure); + furi_string_free(code_part); + + //draw the ending guard pattern + x = draw_bits(canvas, end_bits, x, y, width, height + 5); +} + +/** + * Draw a UPC-A barcode +*/ +static void draw_upc_a(Canvas* canvas, BarcodeData* barcode_data) { + FuriString* barcode_digits = barcode_data->correct_data; + BarcodeTypeObj* type_obj = barcode_data->type_obj; + + int barcode_length = furi_string_size(barcode_digits); + + int x = type_obj->start_pos; + int y = BARCODE_Y_START; + int width = 1; + int height = BARCODE_HEIGHT; + + //the guard patterns for the beginning, center, ending + char* end_bits = "101"; + char* center_bits = "01010"; + + //draw the starting guard pattern + x = draw_bits(canvas, end_bits, x, y, width, height + 5); + + FuriString* code_part = furi_string_alloc(); + + //loop through each digit, find the encoding, and draw it + for(int i = 0; i < barcode_length; i++) { + char current_digit = furi_string_get_char(barcode_digits, i); + int index = current_digit - '0'; //convert the number into an int (also the index) + + //use the L-codes for the first 6 digits and the R-Codes for the last 6 digits + if(i <= 5) { + furi_string_set_str(code_part, UPC_EAN_L_CODES[index]); + } else { + furi_string_set_str(code_part, UPC_EAN_R_CODES[index]); + } + + //convert the current_digit char into a string so it can be printed + char current_digit_string[2]; + snprintf(current_digit_string, 2, "%c", current_digit); + + //set the canvas color to black to print the digit + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, x + 1, y + height + 8, current_digit_string); + + //draw the bits of the barcode + x = draw_bits(canvas, furi_string_get_cstr(code_part), x, y, width, height); + + //if the index has reached 6, that means 6 digits have been drawn and we now draw the center guard pattern + if(i == 5) { + x = draw_bits(canvas, center_bits, x, y, width, height + 5); + } + } + + furi_string_free(code_part); + + //draw the ending guard pattern + x = draw_bits(canvas, end_bits, x, y, width, height + 5); +} + +static void draw_code_39(Canvas* canvas, BarcodeData* barcode_data) { + FuriString* raw_data = barcode_data->raw_data; + FuriString* barcode_digits = barcode_data->correct_data; + //BarcodeTypeObj* type_obj = barcode_data->type_obj; + + int barcode_length = furi_string_size(barcode_digits); + int total_pixels = 0; + + for(int i = 0; i < barcode_length; i++) { + //1 for wide, 0 for narrow + char wide_or_narrow = furi_string_get_char(barcode_digits, i); + int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit + + if(wn_digit == 1) { + total_pixels += 3; + } else { + total_pixels += 1; + } + if((i + 1) % 9 == 0) { + total_pixels += 1; + } + } + + int x = (128 - total_pixels) / 2; + int y = BARCODE_Y_START; + int width = 1; + int height = BARCODE_HEIGHT; + bool filled_in = true; + + //set the canvas color to black to print the digit + canvas_set_color(canvas, ColorBlack); + // canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error); + canvas_draw_str_aligned( + canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data)); + + for(int i = 0; i < barcode_length; i++) { + //1 for wide, 0 for narrow + char wide_or_narrow = furi_string_get_char(barcode_digits, i); + int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit + + if(filled_in) { + if(wn_digit == 1) { + x = draw_bits(canvas, "111", x, y, width, height); + } else { + x = draw_bits(canvas, "1", x, y, width, height); + } + filled_in = false; + } else { + if(wn_digit == 1) { + x = draw_bits(canvas, "000", x, y, width, height); + } else { + x = draw_bits(canvas, "0", x, y, width, height); + } + filled_in = true; + } + if((i + 1) % 9 == 0) { + x = draw_bits(canvas, "0", x, y, width, height); + filled_in = true; + } + } +} + +static void draw_code_128(Canvas* canvas, BarcodeData* barcode_data) { + FuriString* raw_data = barcode_data->raw_data; + FuriString* barcode_digits = barcode_data->correct_data; + + int barcode_length = furi_string_size(barcode_digits); + + int x = (128 - barcode_length) / 2; + int y = BARCODE_Y_START; + int width = 1; + int height = BARCODE_HEIGHT; + + x = draw_bits(canvas, furi_string_get_cstr(barcode_digits), x, y, width, height); + + //set the canvas color to black to print the digit + canvas_set_color(canvas, ColorBlack); + // canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error); + canvas_draw_str_aligned( + canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data)); +} + +static void barcode_draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + BarcodeModel* barcode_model = ctx; + BarcodeData* data = barcode_model->data; + // const char* barcode_digits =; + + canvas_clear(canvas); + if(data->valid) { + switch(data->type_obj->type) { + case UPCA: + draw_upc_a(canvas, data); + break; + case EAN8: + draw_ean_8(canvas, data); + break; + case EAN13: + draw_ean_13(canvas, data); + break; + case CODE39: + draw_code_39(canvas, data); + break; + case CODE128: + draw_code_128(canvas, data); + break; + case UNKNOWN: + default: + break; + } + } else { + switch(data->reason) { + case WrongNumberOfDigits: + draw_error_str(canvas, "Wrong # of characters"); + break; + case InvalidCharacters: + draw_error_str(canvas, "Invalid characters"); + break; + case UnsupportedType: + draw_error_str(canvas, "Unsupported barcode type"); + break; + case FileOpening: + draw_error_str(canvas, "Could not open file"); + break; + case InvalidFileData: + draw_error_str(canvas, "Invalid file data"); + break; + case MissingEncodingTable: + draw_error_str(canvas, "Missing encoding table"); + break; + case EncodingTableError: + draw_error_str(canvas, "Encoding table error"); + break; + default: + draw_error_str(canvas, "Could not read barcode data"); + break; + } + } +} + +bool barcode_input_callback(InputEvent* input_event, void* ctx) { + UNUSED(ctx); + //furi_assert(ctx); + + //Barcode* test_view_object = ctx; + + if(input_event->key == InputKeyBack) { + return false; + } else { + return true; + } +} + +Barcode* barcode_view_allocate(BarcodeApp* barcode_app) { + furi_assert(barcode_app); + + Barcode* barcode = malloc(sizeof(Barcode)); + + barcode->view = view_alloc(); + barcode->barcode_app = barcode_app; + + view_set_context(barcode->view, barcode); + view_allocate_model(barcode->view, ViewModelTypeLocking, sizeof(BarcodeModel)); + view_set_draw_callback(barcode->view, barcode_draw_callback); + view_set_input_callback(barcode->view, barcode_input_callback); + + return barcode; +} + +void barcode_free_model(Barcode* barcode) { + with_view_model( + barcode->view, + BarcodeModel * model, + { + if(model->file_path != NULL) { + furi_string_free(model->file_path); + } + if(model->data != NULL) { + if(model->data->raw_data != NULL) { + furi_string_free(model->data->raw_data); + } + if(model->data->correct_data != NULL) { + furi_string_free(model->data->correct_data); + } + free(model->data); + } + }, + false); +} + +void barcode_free(Barcode* barcode) { + furi_assert(barcode); + + barcode_free_model(barcode); + view_free(barcode->view); + free(barcode); +} + +View* barcode_get_view(Barcode* barcode) { + furi_assert(barcode); + return barcode->view; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/barcode_view.h b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/barcode_view.h new file mode 100644 index 000000000..828428c08 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/barcode_view.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +typedef struct BarcodeApp BarcodeApp; + +typedef struct { + View* view; + BarcodeApp* barcode_app; +} Barcode; + +typedef struct { + FuriString* file_path; + BarcodeData* data; +} BarcodeModel; + +Barcode* barcode_view_allocate(BarcodeApp* barcode_app); + +void barcode_free_model(Barcode* barcode); + +void barcode_free(Barcode* barcode); + +View* barcode_get_view(Barcode* barcode); diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/create_view.c b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/create_view.c new file mode 100644 index 000000000..23a5fa409 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/create_view.c @@ -0,0 +1,493 @@ +#include "../barcode_app.h" +#include "create_view.h" +#include + +#define LINE_HEIGHT 16 +#define TEXT_PADDING 4 +#define TOTAL_MENU_ITEMS 5 + +typedef enum { + TypeMenuItem, + FileNameMenuItem, + BarcodeDataMenuItem, + SaveMenuButton, + DeleteMenuButton +} MenuItems; + +/** + * Took this function from blackjack + * @author @teeebor +*/ +void draw_menu_item( + Canvas* const canvas, + const char* text, + const char* value, + int y, + bool left_caret, + bool right_caret, + bool selected) { + UNUSED(selected); + if(y < 0 || y >= 64) { + return; + } + + if(selected) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, y, 123, LINE_HEIGHT); + canvas_set_color(canvas, ColorWhite); + } + + canvas_draw_str_aligned(canvas, 4, y + TEXT_PADDING, AlignLeft, AlignTop, text); + if(left_caret) { + canvas_draw_str_aligned(canvas, 60, y + TEXT_PADDING, AlignLeft, AlignTop, "<"); + } + + canvas_draw_str_aligned(canvas, 90, y + TEXT_PADDING, AlignCenter, AlignTop, value); + if(right_caret) { + canvas_draw_str_aligned(canvas, 120, y + TEXT_PADDING, AlignRight, AlignTop, ">"); + } + + canvas_set_color(canvas, ColorBlack); +} + +void draw_button(Canvas* const canvas, const char* text, int y, bool selected) { + if(selected) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, y, 123, LINE_HEIGHT); + canvas_set_color(canvas, ColorWhite); + } + + canvas_draw_str_aligned(canvas, 64, y + TEXT_PADDING, AlignCenter, AlignTop, text); + + canvas_set_color(canvas, ColorBlack); +} + +static void app_draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + + CreateViewModel* create_view_model = ctx; + + BarcodeTypeObj* type_obj = create_view_model->barcode_type; + if(create_view_model->barcode_type == NULL) { + return; + } + BarcodeType selected_type = type_obj->type; + + int selected_menu_item = create_view_model->selected_menu_item; + + int total_menu_items = create_view_model->mode == EditMode ? TOTAL_MENU_ITEMS : + TOTAL_MENU_ITEMS - 1; + + int startY = 0; + + //the menu items index that is/would be in view + //int current_last_menu_item = selected_menu_item + 3; + if(selected_menu_item > 1) { + int offset = 2; + if(selected_menu_item + offset > total_menu_items) { + offset = 3; + } + startY -= (LINE_HEIGHT * (selected_menu_item - offset)); + } + + //ensure that the scroll height is atleast 1 + int scrollHeight = ceil(64.0 / total_menu_items); + int scrollPos = scrollHeight * selected_menu_item; + + canvas_set_color(canvas, ColorBlack); + //draw the scroll bar box + canvas_draw_box(canvas, 125, scrollPos, 3, scrollHeight); + //draw the scroll bar track + canvas_draw_box(canvas, 126, 0, 1, 64); + + draw_menu_item( + canvas, + "Type", + type_obj->name, + TypeMenuItem * LINE_HEIGHT + startY, + selected_type > 0, + selected_type < NUMBER_OF_BARCODE_TYPES - 2, + selected_menu_item == TypeMenuItem); + + draw_menu_item( + canvas, + "Name", + furi_string_empty(create_view_model->file_name) ? + "--" : + furi_string_get_cstr(create_view_model->file_name), + FileNameMenuItem * LINE_HEIGHT + startY, + false, + false, + selected_menu_item == FileNameMenuItem); + + draw_menu_item( + canvas, + "Data", + furi_string_empty(create_view_model->barcode_data) ? + "--" : + furi_string_get_cstr(create_view_model->barcode_data), + BarcodeDataMenuItem * LINE_HEIGHT + startY, + false, + false, + selected_menu_item == BarcodeDataMenuItem); + + draw_button( + canvas, + "Save", + SaveMenuButton * LINE_HEIGHT + startY, + selected_menu_item == SaveMenuButton); + + if(create_view_model->mode == EditMode) { + draw_button( + canvas, + "Delete", + DeleteMenuButton * LINE_HEIGHT + startY, + selected_menu_item == DeleteMenuButton); + } +} + +void text_input_callback(void* ctx) { + CreateView* create_view_object = ctx; + + with_view_model( + create_view_object->view, + CreateViewModel * model, + { + if(create_view_object->setter == FileNameSetter) { + furi_string_set_str(model->file_name, create_view_object->input); + } + if(create_view_object->setter == BarcodeDataSetter) { + furi_string_set_str(model->barcode_data, create_view_object->input); + } + }, + true); + + view_dispatcher_switch_to_view( + create_view_object->barcode_app->view_dispatcher, CreateBarcodeView); +} + +static bool app_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + + if(input_event->key == InputKeyBack) { + return false; + } + + CreateView* create_view_object = ctx; + + //get the currently selected menu item from the model + int selected_menu_item = 0; + BarcodeTypeObj* barcode_type = NULL; + FuriString* file_name; + FuriString* barcode_data; + CreateMode mode; + + with_view_model( + create_view_object->view, + CreateViewModel * model, + { + selected_menu_item = model->selected_menu_item; + barcode_type = model->barcode_type; + file_name = model->file_name; + barcode_data = model->barcode_data; + mode = model->mode; + }, + true); + + int total_menu_items = mode == EditMode ? TOTAL_MENU_ITEMS : TOTAL_MENU_ITEMS - 1; + + if(input_event->type == InputTypePress) { + if(input_event->key == InputKeyUp && selected_menu_item > 0) { + selected_menu_item--; + } else if(input_event->key == InputKeyDown && selected_menu_item < total_menu_items - 1) { + selected_menu_item++; + } else if(input_event->key == InputKeyLeft) { + if(selected_menu_item == TypeMenuItem && barcode_type != NULL) { //Select Barcode Type + if(barcode_type->type > 0) { + barcode_type = barcode_type_objs[barcode_type->type - 1]; + } + } + } else if(input_event->key == InputKeyRight) { + if(selected_menu_item == TypeMenuItem && barcode_type != NULL) { //Select Barcode Type + if(barcode_type->type < NUMBER_OF_BARCODE_TYPES - 2) { + barcode_type = barcode_type_objs[barcode_type->type + 1]; + } + } + } else if(input_event->key == InputKeyOk) { + if(selected_menu_item == FileNameMenuItem && barcode_type != NULL) { + create_view_object->setter = FileNameSetter; + + snprintf( + create_view_object->input, + sizeof(create_view_object->input), + "%s", + furi_string_get_cstr(file_name)); + + text_input_set_result_callback( + create_view_object->barcode_app->text_input, + text_input_callback, + create_view_object, + create_view_object->input, + TEXT_BUFFER_SIZE - BARCODE_EXTENSION_LENGTH, //remove the barcode length + //clear default text + false); + text_input_set_header_text( + create_view_object->barcode_app->text_input, "File Name"); + + view_dispatcher_switch_to_view( + create_view_object->barcode_app->view_dispatcher, TextInputView); + } + if(selected_menu_item == BarcodeDataMenuItem && barcode_type != NULL) { + create_view_object->setter = BarcodeDataSetter; + + snprintf( + create_view_object->input, + sizeof(create_view_object->input), + "%s", + furi_string_get_cstr(barcode_data)); + + text_input_set_result_callback( + create_view_object->barcode_app->text_input, + text_input_callback, + create_view_object, + create_view_object->input, + TEXT_BUFFER_SIZE, + //clear default text + false); + text_input_set_header_text( + create_view_object->barcode_app->text_input, "Barcode Data"); + + view_dispatcher_switch_to_view( + create_view_object->barcode_app->view_dispatcher, TextInputView); + } + if(selected_menu_item == SaveMenuButton && barcode_type != NULL) { + save_barcode(create_view_object); + } + if(selected_menu_item == DeleteMenuButton && barcode_type != NULL) { + if(mode == EditMode) { + remove_barcode(create_view_object); + } else if(mode == NewMode) { + view_dispatcher_switch_to_view( + create_view_object->barcode_app->view_dispatcher, MainMenuView); + } + } + } + } + + //change the currently selected menu item + with_view_model( + create_view_object->view, + CreateViewModel * model, + { + model->selected_menu_item = selected_menu_item; + model->barcode_type = barcode_type; + }, + true); + + return true; +} + +CreateView* create_view_allocate(BarcodeApp* barcode_app) { + furi_assert(barcode_app); + + CreateView* create_view_object = malloc(sizeof(CreateView)); + + create_view_object->view = view_alloc(); + create_view_object->barcode_app = barcode_app; + + view_set_context(create_view_object->view, create_view_object); + view_allocate_model(create_view_object->view, ViewModelTypeLocking, sizeof(CreateViewModel)); + view_set_draw_callback(create_view_object->view, app_draw_callback); + view_set_input_callback(create_view_object->view, app_input_callback); + + return create_view_object; +} + +void create_view_free_model(CreateView* create_view_object) { + with_view_model( + create_view_object->view, + CreateViewModel * model, + { + if(model->file_path != NULL) { + furi_string_free(model->file_path); + } + if(model->file_name != NULL) { + furi_string_free(model->file_name); + } + if(model->barcode_data != NULL) { + furi_string_free(model->barcode_data); + } + }, + true); +} + +void remove_barcode(CreateView* create_view_object) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool success = false; + + with_view_model( + create_view_object->view, + CreateViewModel * model, + { + FURI_LOG_I(TAG, "Attempting to remove file"); + if(model->file_path != NULL) { + FURI_LOG_I(TAG, "Removing File: %s", furi_string_get_cstr(model->file_path)); + if(storage_simply_remove(storage, furi_string_get_cstr(model->file_path))) { + FURI_LOG_I( + TAG, + "File: \"%s\" was successfully removed", + furi_string_get_cstr(model->file_path)); + success = true; + } else { + FURI_LOG_E(TAG, "Unable to remove file!"); + success = false; + } + } else { + FURI_LOG_E(TAG, "Could not remove barcode file"); + success = false; + } + }, + true); + furi_record_close(RECORD_STORAGE); + + with_view_model( + create_view_object->barcode_app->message_view->view, + MessageViewModel * model, + { + if(success) { + model->message = "File Deleted"; + } else { + model->message = "Could not delete file"; + } + }, + true); + + view_dispatcher_switch_to_view( + create_view_object->barcode_app->view_dispatcher, MessageErrorView); +} + +void save_barcode(CreateView* create_view_object) { + BarcodeTypeObj* barcode_type = NULL; + FuriString* file_path; //this may be empty + FuriString* file_name; + FuriString* barcode_data; + CreateMode mode; + + with_view_model( + create_view_object->view, + CreateViewModel * model, + { + file_path = model->file_path; + file_name = model->file_name; + barcode_data = model->barcode_data; + barcode_type = model->barcode_type; + mode = model->mode; + }, + true); + + if(file_name == NULL || furi_string_empty(file_name)) { + FURI_LOG_E(TAG, "File Name cannot be empty"); + return; + } + if(barcode_data == NULL || furi_string_empty(barcode_data)) { + FURI_LOG_E(TAG, "Barcode Data cannot be empty"); + return; + } + if(barcode_type == NULL) { + FURI_LOG_E(TAG, "Type not defined"); + return; + } + + bool success = false; + + FuriString* full_file_path = furi_string_alloc_set(DEFAULT_USER_BARCODES); + furi_string_push_back(full_file_path, '/'); + furi_string_cat(full_file_path, file_name); + furi_string_cat_str(full_file_path, BARCODE_EXTENSION); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(mode == EditMode) { + if(!furi_string_empty(file_path)) { + if(!furi_string_equal(file_path, full_file_path)) { + FS_Error error = storage_common_rename( + storage, + furi_string_get_cstr(file_path), + furi_string_get_cstr(full_file_path)); + if(error != FSE_OK) { + FURI_LOG_E(TAG, "Rename error: %s", storage_error_get_desc(error)); + } else { + FURI_LOG_I(TAG, "Rename Success"); + } + } + } + } + + FlipperFormat* ff = flipper_format_file_alloc(storage); + + FURI_LOG_I(TAG, "Saving Barcode to: %s", furi_string_get_cstr(full_file_path)); + + bool file_opened_status = false; + if(mode == NewMode) { + file_opened_status = + flipper_format_file_open_new(ff, furi_string_get_cstr(full_file_path)); + } else if(mode == EditMode) { + file_opened_status = + flipper_format_file_open_always(ff, furi_string_get_cstr(full_file_path)); + } + + if(file_opened_status) { + // Filetype: Barcode + // Version: 1 + + // # Types - UPC-A, EAN-8, EAN-13, CODE-39 + // Type: CODE-39 + // Data: AB + flipper_format_write_string_cstr(ff, "Filetype", "Barcode"); + + flipper_format_write_string_cstr(ff, "Version", FILE_VERSION); + + flipper_format_write_comment_cstr(ff, "Types - UPC-A, EAN-8, EAN-13, CODE-39, CODE-128"); + + flipper_format_write_string_cstr(ff, "Type", barcode_type->name); + + flipper_format_write_string_cstr(ff, "Data", furi_string_get_cstr(barcode_data)); + + success = true; + } else { + FURI_LOG_E(TAG, "Save error"); + success = false; + } + furi_string_free(full_file_path); + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + with_view_model( + create_view_object->barcode_app->message_view->view, + MessageViewModel * model, + { + if(success) { + model->message = "File Saved!"; + } else { + model->message = "A saving error has occurred"; + } + }, + true); + + view_dispatcher_switch_to_view( + create_view_object->barcode_app->view_dispatcher, MessageErrorView); +} + +void create_view_free(CreateView* create_view_object) { + furi_assert(create_view_object); + + create_view_free_model(create_view_object); + view_free(create_view_object->view); + free(create_view_object); +} + +View* create_get_view(CreateView* create_view_object) { + furi_assert(create_view_object); + return create_view_object->view; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/create_view.h b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/create_view.h new file mode 100644 index 000000000..6063786d9 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/create_view.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +typedef struct BarcodeApp BarcodeApp; + +typedef enum { + FileNameSetter, + BarcodeDataSetter +} InputSetter; //what value to set for the text input view + +typedef enum { + EditMode, + + NewMode +} CreateMode; + +typedef struct { + View* view; + BarcodeApp* barcode_app; + + InputSetter setter; + char input[TEXT_BUFFER_SIZE]; +} CreateView; + +typedef struct { + int selected_menu_item; + + CreateMode mode; + BarcodeTypeObj* barcode_type; + FuriString* file_path; //the current file that is opened + FuriString* file_name; + FuriString* barcode_data; +} CreateViewModel; + +CreateView* create_view_allocate(BarcodeApp* barcode_app); + +void remove_barcode(CreateView* create_view_object); + +void save_barcode(CreateView* create_view_object); + +void create_view_free_model(CreateView* create_view_object); + +void create_view_free(CreateView* create_view_object); + +View* create_get_view(CreateView* create_view_object); diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/message_view.c b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/message_view.c new file mode 100644 index 000000000..0001e98e6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/message_view.c @@ -0,0 +1,77 @@ +#include "../barcode_app.h" +#include "message_view.h" + +static void app_draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + + MessageViewModel* message_view_model = ctx; + + canvas_clear(canvas); + if(message_view_model->message != NULL) { + canvas_draw_str_aligned( + canvas, 62, 30, AlignCenter, AlignCenter, message_view_model->message); + } + + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 100, 52, 28, 12); + canvas_set_color(canvas, ColorWhite); + canvas_draw_str_aligned(canvas, 114, 58, AlignCenter, AlignCenter, "OK"); +} + +static bool app_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + + MessageView* message_view_object = ctx; + + if(input_event->key == InputKeyBack) { + view_dispatcher_switch_to_view( + message_view_object->barcode_app->view_dispatcher, MainMenuView); + } + if(input_event->type == InputTypeShort) { + if(input_event->key == InputKeyOk) { + view_dispatcher_switch_to_view( + message_view_object->barcode_app->view_dispatcher, MainMenuView); + } + } + + return true; +} + +MessageView* message_view_allocate(BarcodeApp* barcode_app) { + furi_assert(barcode_app); + + MessageView* message_view_object = malloc(sizeof(MessageView)); + + message_view_object->view = view_alloc(); + message_view_object->barcode_app = barcode_app; + + view_set_context(message_view_object->view, message_view_object); + view_allocate_model(message_view_object->view, ViewModelTypeLocking, sizeof(MessageViewModel)); + view_set_draw_callback(message_view_object->view, app_draw_callback); + view_set_input_callback(message_view_object->view, app_input_callback); + + return message_view_object; +} + +void message_view_free_model(MessageView* message_view_object) { + with_view_model( + message_view_object->view, + MessageViewModel * model, + { + + }, + true); +} + +void message_view_free(MessageView* message_view_object) { + furi_assert(message_view_object); + + message_view_free_model(message_view_object); + view_free(message_view_object->view); + free(message_view_object); +} + +View* message_get_view(MessageView* message_view_object) { + furi_assert(message_view_object); + return message_view_object->view; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/message_view.h b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/message_view.h new file mode 100644 index 000000000..33acc3d0c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/barcode_generator/views/message_view.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +typedef struct BarcodeApp BarcodeApp; + +typedef struct { + View* view; + BarcodeApp* barcode_app; +} MessageView; + +typedef struct { + const char* message; +} MessageViewModel; + +MessageView* message_view_allocate(BarcodeApp* barcode_app); + +void message_view_free_model(MessageView* message_view_object); + +void message_view_free(MessageView* message_view_object); + +View* message_get_view(MessageView* message_view_object); diff --git a/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/README.md b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/README.md new file mode 100644 index 000000000..8e88863ee --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/README.md @@ -0,0 +1,14 @@ +# BPM Tapper + +A BPM Tapper for the Flipper Zero. + +![screenshot](img/screenshot.png) + +Hit any button other than back repeatedly. Calculates based on the average of the last 8 inputs. + +## Compiling + +``` +./fbt firmware_bpm_tapper +``` + diff --git a/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/application.fam b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/application.fam new file mode 100644 index 000000000..e256aaed6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/application.fam @@ -0,0 +1,13 @@ +App( + appid="BPM_Tapper", + name="BPM Tapper", + apptype=FlipperAppType.EXTERNAL, + entry_point="bpm_tapper_app", + cdefines=["APP_BPM_TAPPER"], + requires=["gui"], + stack_size=2 * 1024, + fap_icon="bpm_10px.png", + fap_category="Music_Extra", + fap_icon_assets="icons", + order=15, +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/bpm.c b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/bpm.c new file mode 100644 index 000000000..cee83a6a4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/bpm.c @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include +#include +#include "BPM_Tapper_icons.h" + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +//QUEUE + +struct node { + int interval; + struct node* next; +}; +typedef struct node node; + +typedef struct { + int size; + int max_size; + node* front; + node* rear; +} queue; + +static void init_queue(queue* q) { + q->size = 0; + q->max_size = 8; + q->front = NULL; + q->rear = NULL; +} + +static void queue_remove(queue* q) { + node* tmp; + tmp = q->front; + q->front = q->front->next; + q->size--; + free(tmp); +} + +static void queue_add(queue* q, int value) { + node* tmp = malloc(sizeof(node)); + tmp->interval = value; + tmp->next = NULL; + if(q->size == q->max_size) { + queue_remove(q); + } + // check if empty + if(q->rear == NULL) { + q->front = tmp; + q->rear = tmp; + } else { + q->rear->next = tmp; + q->rear = tmp; + } + q->size++; +} + +static float queue_avg(queue* q) { + float avg = 0.0; + if(q->size == 0) { + return avg; + } else { + node* tmp; + float sum = 0.0; + tmp = q->front; + while(tmp != NULL) { + sum = sum + tmp->interval; + tmp = tmp->next; + } + avg = sum / q->size; + FURI_LOG_D("BPM-Tapper", "Sum: %.2f Avg: %.2f", (double)sum, (double)avg); + return avg; + } +} + +// TOO SLOW! +//uint64_t dolphin_state_timestamp() { +// FuriHalRtcDateTime datetime; +// furi_hal_rtc_get_datetime(&datetime); +// return furi_hal_rtc_datetime_to_timestamp(&datetime); +//} +// +typedef struct { + int taps; + double bpm; + uint32_t last_stamp; + uint32_t interval; + queue* tap_queue; +} BPMTapper; + +static void show_hello() { + // BEGIN HELLO DIALOG + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + + const char* header_text = "BPM Tapper"; + const char* message_text = "Tap center to start"; + + dialog_message_set_header(message, header_text, 63, 3, AlignCenter, AlignTop); + dialog_message_set_text(message, message_text, 0, 17, AlignLeft, AlignTop); + dialog_message_set_buttons(message, NULL, "Tap", NULL); + + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); + + dialog_message_show(dialogs, message); + + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + // END HELLO DIALOG +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void render_callback(Canvas* const canvas, void* ctx) { + FuriString* tempStr; + + const BPMTapper* bpm_state = acquire_mutex((ValueMutex*)ctx, 25); + if(bpm_state == NULL) { + return; + } + // border + //canvas_draw_frame(canvas, 0, 0, 128, 64); + canvas_set_font(canvas, FontPrimary); + + tempStr = furi_string_alloc(); + + furi_string_printf(tempStr, "Taps: %d", bpm_state->taps); + canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); + + furi_string_printf(tempStr, "Queue: %d", bpm_state->tap_queue->size); + canvas_draw_str_aligned(canvas, 70, 10, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); + + furi_string_printf(tempStr, "Interval: %ldms", bpm_state->interval); + canvas_draw_str_aligned(canvas, 5, 20, AlignLeft, AlignBottom, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); + + furi_string_printf(tempStr, "x2 %.2f /2 %.2f", bpm_state->bpm * 2, bpm_state->bpm / 2); + canvas_draw_str_aligned( + canvas, 64, 60, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); + + furi_string_printf(tempStr, "%.2f", bpm_state->bpm); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned( + canvas, 64, 40, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); + + furi_string_free(tempStr); + + release_mutex((ValueMutex*)ctx, bpm_state); +} + +static void bpm_state_init(BPMTapper* const plugin_state) { + plugin_state->taps = 0; + plugin_state->bpm = 120.0; + plugin_state->last_stamp = 0; // furi_get_tick(); + plugin_state->interval = 0; + queue* q; + q = malloc(sizeof(queue)); + init_queue(q); + plugin_state->tap_queue = q; +} + +int32_t bpm_tapper_app(void* p) { + UNUSED(p); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + BPMTapper* bpm_state = malloc(sizeof(BPMTapper)); + // setup + bpm_state_init(bpm_state); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, bpm_state, sizeof(bpm_state))) { + FURI_LOG_E("BPM-Tapper", "cannot create mutex\r\n"); + free(bpm_state); + return 255; + } + show_hello(); + + // BEGIN IMPLEMENTATION + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + BPMTapper* bpm_state = (BPMTapper*)acquire_mutex_block(&state_mutex); + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + case InputKeyDown: + case InputKeyRight: + case InputKeyLeft: + case InputKeyOk: + bpm_state->taps++; + uint32_t new_stamp = furi_get_tick(); + if(bpm_state->last_stamp == 0) { + bpm_state->last_stamp = new_stamp; + break; + } + bpm_state->interval = new_stamp - bpm_state->last_stamp; + bpm_state->last_stamp = new_stamp; + queue_add(bpm_state->tap_queue, bpm_state->interval); + float avg = queue_avg(bpm_state->tap_queue); + float bps = 1.0 / (avg / 1000.0); + bpm_state->bpm = bps * 60.0; + break; + case InputKeyBack: + // Exit the plugin + processing = false; + break; + default: + break; + } + } + } + } else { + FURI_LOG_D("BPM-Tapper", "FuriMessageQueue: event timeout"); + // event timeout + } + view_port_update(view_port); + release_mutex(&state_mutex, bpm_state); + } + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + queue* q = bpm_state->tap_queue; + free(q); + free(bpm_state); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/bpm_10px.png b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/bpm_10px.png new file mode 100644 index 000000000..ebf27486c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/bpm_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/icons/DolphinCommon_56x48.png b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/icons/DolphinCommon_56x48.png new file mode 100644 index 000000000..089aaed83 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/icons/DolphinCommon_56x48.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/images/DolphinCommon_56x48.png b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/images/DolphinCommon_56x48.png new file mode 100644 index 000000000..089aaed83 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/images/DolphinCommon_56x48.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/img/screenshot.png b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/img/screenshot.png new file mode 100644 index 000000000..fbba2aad9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/bpmtapper/img/screenshot.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/README.md b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/README.md new file mode 100644 index 000000000..bb62a4298 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/README.md @@ -0,0 +1,10 @@ +# FlipperZeroBrainfuck + +Brainfuck interpreter and editor for the F0. +Supports text inputs and outputs. +No protection against infinite loops or syntax errors. +Major limitation is that programs MUST terminate, or it will freeze at "RUNNING" + +![Screenshot-20230117-202147](https://user-images.githubusercontent.com/16545187/213004616-8846e897-506e-4510-8012-fd2fe2bbe8a1.png) + +![Screenshot-20230117-202208](https://user-images.githubusercontent.com/16545187/213004659-d74751d2-76c4-4a7b-a0f2-f58623478b95.png) diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/application.fam b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/application.fam new file mode 100644 index 000000000..4d9c0e741 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/application.fam @@ -0,0 +1,14 @@ +App( + appid="Brainfuck", + name="Brainfuck", + apptype=FlipperAppType.EXTERNAL, + entry_point="brainfuck_app", + requires=[ + "storage", + "gui", + ], + stack_size=8 * 1024, + fap_icon="bfico.png", + fap_category="Misc_Extra", + fap_icon_assets="icons", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/bfico.png b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/bfico.png new file mode 100644 index 000000000..b25368fb5 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/bfico.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/brainfuck.c b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/brainfuck.c new file mode 100644 index 000000000..15e838bd1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/brainfuck.c @@ -0,0 +1,137 @@ +#include "brainfuck_i.h" + +/* + Due to the lack of documentation on the flipper i copied the picopass app, + ripped its insides out and used its hollow corpse to build this app inside of. + + i dont know how this stuff works and after 6 hours of trying to learn it, i dont care +*/ + +bool brainfuck_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + BFApp* brainfuck = context; + return scene_manager_handle_custom_event(brainfuck->scene_manager, event); +} + +bool brainfuck_back_event_callback(void* context) { + furi_assert(context); + BFApp* brainfuck = context; + return scene_manager_handle_back_event(brainfuck->scene_manager); +} + +BFApp* brainfuck_alloc() { + BFApp* brainfuck = malloc(sizeof(BFApp)); + + brainfuck->dataSize = 0; + brainfuck->view_dispatcher = view_dispatcher_alloc(); + brainfuck->scene_manager = scene_manager_alloc(&brainfuck_scene_handlers, brainfuck); + view_dispatcher_enable_queue(brainfuck->view_dispatcher); + view_dispatcher_set_event_callback_context(brainfuck->view_dispatcher, brainfuck); + view_dispatcher_set_custom_event_callback(brainfuck->view_dispatcher, brainfuck_custom_event_callback); + view_dispatcher_set_navigation_event_callback(brainfuck->view_dispatcher, brainfuck_back_event_callback); + + // Open GUI record + brainfuck->gui = furi_record_open(RECORD_GUI); + view_dispatcher_attach_to_gui(brainfuck->view_dispatcher, brainfuck->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + brainfuck->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Submenu + brainfuck->submenu = submenu_alloc(); + view_dispatcher_add_view(brainfuck->view_dispatcher, brainfuckViewMenu, submenu_get_view(brainfuck->submenu)); + + // Popup + brainfuck->popup = popup_alloc(); + view_dispatcher_add_view(brainfuck->view_dispatcher, brainfuckViewPopup, popup_get_view(brainfuck->popup)); + + // Text Input + brainfuck->text_input = text_input_alloc(); + view_dispatcher_add_view(brainfuck->view_dispatcher, brainfuckViewTextInput, text_input_get_view(brainfuck->text_input)); + + // Textbox + brainfuck->text_box = text_box_alloc(); + view_dispatcher_add_view(brainfuck->view_dispatcher, brainfuckViewTextBox, text_box_get_view(brainfuck->text_box)); + brainfuck->text_box_store = furi_string_alloc(); + + // Dev environment + brainfuck->BF_dev_env = bf_dev_env_alloc(brainfuck); + view_dispatcher_add_view(brainfuck->view_dispatcher, brainfuckViewDev, bf_dev_env_get_view(brainfuck->BF_dev_env)); + + // File path + brainfuck->BF_file_path = furi_string_alloc(); + + return brainfuck; +} + +void brainfuck_free(BFApp* brainfuck) { + furi_assert(brainfuck); + + // Submenu + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewMenu); + submenu_free(brainfuck->submenu); + + // Popup + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewPopup); + popup_free(brainfuck->popup); + + // TextInput + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewTextInput); + text_input_free(brainfuck->text_input); + + // TextBox + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewTextBox); + text_box_free(brainfuck->text_box); + furi_string_free(brainfuck->text_box_store); + + //dev env + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewDev); + bf_dev_env_free(brainfuck->BF_dev_env); + + // View Dispatcher + view_dispatcher_free(brainfuck->view_dispatcher); + + // Scene Manager + scene_manager_free(brainfuck->scene_manager); + + // GUI + furi_record_close(RECORD_GUI); + brainfuck->gui = NULL; + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + brainfuck->notifications = NULL; + + free(brainfuck); +} + +void brainfuck_show_loading_popup(void* context, bool show) { + BFApp* brainfuck = context; + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_dispatcher_switch_to_view(brainfuck->view_dispatcher, brainfuckViewLoading); + } else { + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + +int32_t brainfuck_app(void* p) { + UNUSED(p); + BFApp* brainfuck = brainfuck_alloc(); + if(!brainfuck){ return 0; } + + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_mkdir(storage, "/ext/brainfuck"); + + scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneStart); + + view_dispatcher_run(brainfuck->view_dispatcher); + + brainfuck_free(brainfuck); + + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/brainfuck.h b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/brainfuck.h new file mode 100644 index 000000000..2e58321a6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/brainfuck.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct BFApp BFApp; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/brainfuck_i.h b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/brainfuck_i.h new file mode 100644 index 000000000..3e5a956af --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/brainfuck_i.h @@ -0,0 +1,86 @@ +#pragma once + +typedef struct BFDevEnv BFDevEnv; +typedef struct BFExecEnv BFExecEnv; +typedef unsigned char byte; + +#include "brainfuck.h" +#include "worker.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "scenes/brainfuck_scene.h" + +#include "views/bf_dev_env.h" + +#include +#include +#include + +#include +#include +#include +#include + +#define BF_INST_BUFFER_SIZE 2048 +#define BF_OUTPUT_SIZE 512 +#define BF_STACK_INITIAL_SIZE 128 +#define BF_INPUT_BUFFER_SIZE 64 +#define BF_STACK_STEP_SIZE 32 + +enum brainfuckCustomEvent { + // Reserve first 100 events for button types and indexes, starting from 0 + brainfuckCustomEventReserved = 100, + + brainfuckCustomEventViewExit, + brainfuckCustomEventWorkerExit, + brainfuckCustomEventByteInputDone, + brainfuckCustomEventTextInputDone, +}; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +struct BFApp { + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notifications; + SceneManager* scene_manager; + Submenu* submenu; + Popup* popup; + TextInput* text_input; + TextBox* text_box; + FuriString* text_box_store; + FuriString* BF_file_path; + BFDevEnv* BF_dev_env; + int dataSize; + char dataBuffer[BF_INST_BUFFER_SIZE]; + char inputBuffer[BF_INPUT_BUFFER_SIZE]; +}; + +typedef enum { + brainfuckViewMenu, + brainfuckViewPopup, + brainfuckViewLoading, + brainfuckViewTextInput, + brainfuckViewTextBox, + brainfuckViewWidget, + brainfuckViewDev, + brainfuckViewExec, +} brainfuckView; diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/ButtonRightSmall_3x5.png b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/ButtonRightSmall_3x5.png new file mode 100644 index 000000000..b9d5f87db Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/ButtonRightSmall_3x5.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyBackspaceSelected_24x11.png b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyBackspaceSelected_24x11.png new file mode 100644 index 000000000..c79cfb6c6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyBackspaceSelected_24x11.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyBackspace_24x11.png b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyBackspace_24x11.png new file mode 100644 index 000000000..00e66428d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyBackspace_24x11.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyInputSelected_30x11.png b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyInputSelected_30x11.png new file mode 100644 index 000000000..4c04a0856 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyInputSelected_30x11.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyInput_30x11.png b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyInput_30x11.png new file mode 100644 index 000000000..d23e24aaf Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyInput_30x11.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyRunSelected_24x11.png b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyRunSelected_24x11.png new file mode 100644 index 000000000..3ff5ac5e6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyRunSelected_24x11.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyRun_24x11.png b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyRun_24x11.png new file mode 100644 index 000000000..cce46c972 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeyRun_24x11.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeySaveSelected_24x11.png b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeySaveSelected_24x11.png new file mode 100644 index 000000000..eeb3569d3 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeySaveSelected_24x11.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeySave_24x11.png b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeySave_24x11.png new file mode 100644 index 000000000..e7dba987a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/KeySave_24x11.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/bfico.png b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/bfico.png new file mode 100644 index 000000000..b25368fb5 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/icons/bfico.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene.c b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene.c new file mode 100644 index 000000000..90707c926 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene.c @@ -0,0 +1,30 @@ +#include "brainfuck_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const brainfuck_on_enter_handlers[])(void*) = { +#include "brainfuck_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const brainfuck_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "brainfuck_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const brainfuck_on_exit_handlers[])(void* context) = { +#include "brainfuck_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers brainfuck_scene_handlers = { + .on_enter_handlers = brainfuck_on_enter_handlers, + .on_event_handlers = brainfuck_on_event_handlers, + .on_exit_handlers = brainfuck_on_exit_handlers, + .scene_num = brainfuckSceneNum, +}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene.h b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene.h new file mode 100644 index 000000000..f7131a56c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) brainfuckScene##id, +typedef enum { +#include "brainfuck_scene_config.h" + brainfuckSceneNum, +} brainfuckScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers brainfuck_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "brainfuck_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "brainfuck_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "brainfuck_scene_config.h" +#undef ADD_SCENE diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_config.h b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_config.h new file mode 100644 index 000000000..0efc41641 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_config.h @@ -0,0 +1,6 @@ +ADD_SCENE(brainfuck, start, Start) +ADD_SCENE(brainfuck, file_select, FileSelect) +ADD_SCENE(brainfuck, file_create, FileCreate) +ADD_SCENE(brainfuck, dev_env, DevEnv) +ADD_SCENE(brainfuck, exec_env, ExecEnv) +ADD_SCENE(brainfuck, set_input, SetInput) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_dev.c b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_dev.c new file mode 100644 index 000000000..475e9e573 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_dev.c @@ -0,0 +1,16 @@ +#include "../brainfuck_i.h" + +void brainfuck_scene_dev_env_on_enter(void* context) { + BFApp* app = context; + view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewDev); +} + +bool brainfuck_scene_dev_env_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void brainfuck_scene_dev_env_on_exit(void* context) { + UNUSED(context); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_exec.c b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_exec.c new file mode 100644 index 000000000..d344f7271 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_exec.c @@ -0,0 +1,16 @@ +#include "../brainfuck_i.h" + +void brainfuck_scene_exec_env_on_enter(void* context) { + BFApp* app = context; + view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextBox); +} + +bool brainfuck_scene_exec_env_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void brainfuck_scene_exec_env_on_exit(void* context) { + UNUSED(context); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_file_create.c b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_file_create.c new file mode 100644 index 000000000..525fb049e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_file_create.c @@ -0,0 +1,54 @@ +#include "../brainfuck_i.h" + +void file_name_text_input_callback(void* context) { + BFApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, brainfuckCustomEventTextInputDone); +} + +char tmpName[64] = {}; +byte empty[1] = {0x00}; +void brainfuck_scene_file_create_on_enter(void* context) { + BFApp* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "New script name"); + text_input_set_result_callback( + text_input, + file_name_text_input_callback, + app, + tmpName, + 64, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextInput); +} + +bool brainfuck_scene_file_create_on_event(void* context, SceneManagerEvent event) { + BFApp* app = context; + UNUSED(app); + + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == brainfuckCustomEventTextInputDone) { + furi_string_cat_printf(app->BF_file_path, "/ext/brainfuck/%s.b", tmpName); + + //remove old file + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_remove(storage, furi_string_get_cstr(app->BF_file_path)); + + //save new file + Stream* stream = buffered_file_stream_alloc(storage); + buffered_file_stream_open(stream, furi_string_get_cstr(app->BF_file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS); + stream_write(stream, (const uint8_t*)empty, 1); + buffered_file_stream_close(stream); + + //scene_manager_next_scene(app->scene_manager, brainfuckSceneFileSelect); + scene_manager_next_scene(app->scene_manager, brainfuckSceneDevEnv); + } + } + return consumed; +} + +void brainfuck_scene_file_create_on_exit(void* context) { + UNUSED(context); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_file_select.c b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_file_select.c new file mode 100644 index 000000000..5d262d4a5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_file_select.c @@ -0,0 +1,35 @@ +#include "../brainfuck_i.h" + +void brainfuck_scene_file_select_on_enter(void* context) { + BFApp* app = context; + + DialogsApp* dialogs = furi_record_open("dialogs"); + FuriString* path; + path = furi_string_alloc(); + furi_string_set(path, "/ext/brainfuck"); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".b", &I_bfico); + browser_options.base_path = "/ext/brainfuck"; + browser_options.hide_ext = false; + + bool selected = dialog_file_browser_show(dialogs, path, path, &browser_options); + + if(selected){ + furi_string_set(app->BF_file_path, path); + scene_manager_next_scene(app->scene_manager, brainfuckSceneDevEnv); + } + else{ + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, brainfuckSceneStart); + } +} + +bool brainfuck_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void brainfuck_scene_file_select_on_exit(void* context) { + UNUSED(context); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_set_input.c b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_set_input.c new file mode 100644 index 000000000..b6c5f5b9e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_set_input.c @@ -0,0 +1,39 @@ +#include "../brainfuck_i.h" + +void set_input_text_input_callback(void* context) { + BFApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, brainfuckCustomEventTextInputDone); +} + +void brainfuck_scene_set_input_on_enter(void* context) { + BFApp* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Edit input buffer"); + text_input_set_result_callback( + text_input, + set_input_text_input_callback, + app, + app->inputBuffer, + 64, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextInput); +} + +bool brainfuck_scene_set_input_on_event(void* context, SceneManagerEvent event) { + BFApp* app = context; + + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == brainfuckCustomEventTextInputDone) { + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, brainfuckSceneDevEnv); + } + } + return consumed; +} + +void brainfuck_scene_set_input_on_exit(void* context) { + BFApp* app = context; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, brainfuckSceneDevEnv); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_start.c b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_start.c new file mode 100644 index 000000000..876e019a5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/scenes/brainfuck_scene_start.c @@ -0,0 +1,49 @@ +#include "../brainfuck_i.h" +enum SubmenuIndex { + SubmenuIndexNew, + SubmenuIndexOpen, + SubmenuIndexAbout, +}; + +void brainfuck_scene_start_submenu_callback(void* context, uint32_t index) { + BFApp* brainfuck = context; + view_dispatcher_send_custom_event(brainfuck->view_dispatcher, index); +} +void brainfuck_scene_start_on_enter(void* context) { + BFApp* brainfuck = context; + + Submenu* submenu = brainfuck->submenu; + submenu_add_item(submenu, "New", SubmenuIndexNew, brainfuck_scene_start_submenu_callback, brainfuck); + submenu_add_item(submenu, "Open", SubmenuIndexOpen, brainfuck_scene_start_submenu_callback, brainfuck); + submenu_add_item(submenu, "About", SubmenuIndexAbout, brainfuck_scene_start_submenu_callback, brainfuck); + + submenu_set_selected_item(submenu, scene_manager_get_scene_state(brainfuck->scene_manager, brainfuckSceneStart)); + view_dispatcher_switch_to_view(brainfuck->view_dispatcher, brainfuckViewMenu); +} + +bool brainfuck_scene_start_on_event(void* context, SceneManagerEvent event) { + BFApp* brainfuck = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNew) { + scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneFileCreate); + consumed = true; + } else if(event.event == SubmenuIndexOpen) { + scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneFileSelect); + consumed = true; + } else if(event.event == SubmenuIndexAbout) { + text_box_set_text(brainfuck->text_box, "FlipperBrainfuck\n\nAn F0 brainfuck intepretor\nBy github.com/Nymda"); + scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneExecEnv); + consumed = true; + } + scene_manager_set_scene_state(brainfuck->scene_manager, brainfuckSceneStart, event.event); + } + + return consumed; +} + +void brainfuck_scene_start_on_exit(void* context) { + BFApp* brainfuck = context; + submenu_reset(brainfuck->submenu); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/views/bf_dev_env.c b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/views/bf_dev_env.c new file mode 100644 index 000000000..0bc852b72 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/views/bf_dev_env.c @@ -0,0 +1,396 @@ +#include "bf_dev_env.h" +#include + +typedef struct BFDevEnv { + View* view; + DevEnvOkCallback callback; + void* context; + BFApp* appDev; +} BFDevEnv; + +typedef struct { + uint32_t row; + uint32_t col; +} BFDevEnvModel; + +typedef struct{ + int up; + int down; + int left; + int right; +}bMapping; + +static bool bf_dev_process_up(BFDevEnv* devEnv); +static bool bf_dev_process_down(BFDevEnv* devEnv); +static bool bf_dev_process_left(BFDevEnv* devEnv); +static bool bf_dev_process_right(BFDevEnv* devEnv); +static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event); + +BFApp* appDev; + +char bfChars[9] = {'<', '>', '[', ']', '+', '-', '.', ',', 0x00}; + +int selectedButton = 0; +int saveNotifyCountdown = 0; +int execCountdown = 0; + +char dspLine0[25] = {}; +char dspLine1[25] = {}; +char dspLine2[25] = {}; + +static bMapping buttonMappings[12] = { + { 8, 8, 7, 1}, //0 + { 8, 8, 0, 2}, //1 + { 9, 9, 1, 3}, //2 + { 9, 9, 2, 4}, //3 + {10, 10, 3, 5}, //4 + {10, 10, 4, 6}, //5 + {11, 11, 5, 7}, //6 + {11, 11, 6, 0}, //7 + + { 0, 0, 11, 9}, //8 + { 3, 3, 8, 10}, //9 + { 5, 5, 9, 11}, //10 + { 6, 6, 10, 8} //11 +}; + +#define BT_X 14 +#define BT_Y 14 +static void bf_dev_draw_button(Canvas* canvas, int x, int y, bool selected, const char* lbl){ + UNUSED(lbl); + + if(selected){ + canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3); + canvas_invert_color(canvas); + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned(canvas, x + (BT_X/2), y + (BT_Y/2) - 1, AlignCenter, AlignCenter, lbl); + canvas_invert_color(canvas); + } + else{ + canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3); + canvas_invert_color(canvas); + canvas_draw_rbox(canvas, x+2, y-1, BT_X - 2, BT_Y - 1, 3); + canvas_invert_color(canvas); + canvas_draw_rframe(canvas, x, y, BT_X, BT_Y, 3); + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned(canvas, x + (BT_X/2), y + (BT_Y/2) - 1, AlignCenter, AlignCenter, lbl); + } +} + +static void bf_dev_draw_callback(Canvas* canvas, void* _model) { + UNUSED(_model); + + if(execCountdown > 0){ + execCountdown--; + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "RUNNING..."); + if(execCountdown == 0){ + initWorker(appDev); + beginWorker(); + text_box_set_text(appDev->text_box, workerGetOutput()); + scene_manager_next_scene(appDev->scene_manager, brainfuckSceneExecEnv); + } + return; + } + + if(saveNotifyCountdown > 0){ + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "SAVED"); + saveNotifyCountdown--; + return; + } + + bf_dev_draw_button(canvas, 1, 36, (selectedButton == 0), "+"); //T 0 + bf_dev_draw_button(canvas, 17, 36, (selectedButton == 1), "-"); //T 1 + bf_dev_draw_button(canvas, 33, 36, (selectedButton == 2), "<"); //T 2 + bf_dev_draw_button(canvas, 49, 36, (selectedButton == 3), ">"); //T 3 + bf_dev_draw_button(canvas, 65, 36, (selectedButton == 4), "["); //B 0 + bf_dev_draw_button(canvas, 81, 36, (selectedButton == 5), "]"); //B 1 + bf_dev_draw_button(canvas, 97, 36, (selectedButton == 6), "."); //B 2 + bf_dev_draw_button(canvas, 113, 36, (selectedButton == 7), ","); //B 3 + + //backspace, input, run, save + canvas_draw_icon(canvas, 1, 52, (selectedButton == 8) ? &I_KeyBackspaceSelected_24x11 : &I_KeyBackspace_24x11); + canvas_draw_icon(canvas, 45, 52, (selectedButton == 9) ? &I_KeyInputSelected_30x11 : &I_KeyInput_30x11); + canvas_draw_icon(canvas, 77, 52, (selectedButton == 10) ? &I_KeyRunSelected_24x11 : &I_KeyRun_24x11); + canvas_draw_icon(canvas, 103, 52, (selectedButton == 11) ? &I_KeySaveSelected_24x11 : &I_KeySave_24x11); + + if(saveNotifyCountdown > 0){ + canvas_draw_icon(canvas, 98, 54, &I_ButtonRightSmall_3x5); + saveNotifyCountdown--; + } + + //textbox + //grossly overcomplicated. not fixing it. + canvas_draw_rframe(canvas, 1, 1, 125, 33, 2); + canvas_set_font(canvas, FontBatteryPercent); + + int dbOffset = 0; + if(appDev->dataSize > 72){ + dbOffset = (appDev->dataSize - 72); + } + + memset(dspLine0, 0x00, 25); + memset(dspLine1, 0x00, 25); + memset(dspLine2, 0x00, 25); + + int tpM = 0; + int tp0 = 0; + int tp1 = 0; + int tp2 = 0; + + for(int p = dbOffset; p < appDev->dataSize; p++){ + if(tpM < 24 * 1){ + dspLine0[tp0] = appDev->dataBuffer[p]; + tp0++; + } + else if(tpM < 24 * 2){ + dspLine1[tp1] = appDev->dataBuffer[p]; + tp1++; + } + else if(tpM < 24 * 3){ + dspLine2[tp2] = appDev->dataBuffer[p]; + tp2++; + } + tpM++; + } + + canvas_draw_str_aligned(canvas, 3, 8, AlignLeft, AlignCenter, dspLine0); + canvas_draw_str_aligned(canvas, 3, 17, AlignLeft, AlignCenter, dspLine1); + canvas_draw_str_aligned(canvas, 3, 26, AlignLeft, AlignCenter, dspLine2); +} + + +static bool bf_dev_input_callback(InputEvent* event, void* context) { + furi_assert(context); + BFDevEnv* devEnv = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyRight) { + consumed = bf_dev_process_right(devEnv); + } else if(event->key == InputKeyLeft) { + consumed = bf_dev_process_left(devEnv); + } else if(event->key == InputKeyUp) { + consumed = bf_dev_process_up(devEnv); + } else if(event->key == InputKeyDown) { + consumed = bf_dev_process_down(devEnv); + } + } else if(event->key == InputKeyOk) { + consumed = bf_dev_process_ok(devEnv, event); + } + + return consumed; +} + +static bool bf_dev_process_up(BFDevEnv* devEnv) { + UNUSED(devEnv); + selectedButton = buttonMappings[selectedButton].up; + return true; +} + +static bool bf_dev_process_down(BFDevEnv* devEnv) { + UNUSED(devEnv); + selectedButton = buttonMappings[selectedButton].down; + return true; +} + +static bool bf_dev_process_left(BFDevEnv* devEnv) { + UNUSED(devEnv); + selectedButton = buttonMappings[selectedButton].left; + return true; +} + +static bool bf_dev_process_right(BFDevEnv* devEnv) { + UNUSED(devEnv); + selectedButton = buttonMappings[selectedButton].right; + return true; +} + +static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event) { + UNUSED(devEnv); + UNUSED(event); + + if(event->type != InputTypePress){ return false; } + + switch(selectedButton){ + case 0: + { + if(appDev->dataSize < BF_INST_BUFFER_SIZE){ + appDev->dataBuffer[appDev->dataSize] = (uint32_t)'+'; + appDev->dataSize++; } + break; + } + + case 1: + { + if(appDev->dataSize < BF_INST_BUFFER_SIZE){ + appDev->dataBuffer[appDev->dataSize] = (uint32_t)'-'; + appDev->dataSize++; } + break; + } + + case 2: + { + if(appDev->dataSize < BF_INST_BUFFER_SIZE){ + appDev->dataBuffer[appDev->dataSize] = (uint32_t)'<'; + appDev->dataSize++; } + break; + } + + case 3: + { + if(appDev->dataSize < BF_INST_BUFFER_SIZE){ + appDev->dataBuffer[appDev->dataSize] = (uint32_t)'>'; + appDev->dataSize++; } + break; + } + + case 4: + { + if(appDev->dataSize < BF_INST_BUFFER_SIZE){ + appDev->dataBuffer[appDev->dataSize] = (uint32_t)'['; + appDev->dataSize++; } + break; + } + + case 5: + { + if(appDev->dataSize < BF_INST_BUFFER_SIZE){ + appDev->dataBuffer[appDev->dataSize] = (uint32_t)']'; + appDev->dataSize++; } + break; + } + + case 6: + { + if(appDev->dataSize < BF_INST_BUFFER_SIZE){ + appDev->dataBuffer[appDev->dataSize] = (uint32_t)'.'; + appDev->dataSize++; } + break; + } + + case 7: + { + if(appDev->dataSize < BF_INST_BUFFER_SIZE){ + appDev->dataBuffer[appDev->dataSize] = (uint32_t)','; + appDev->dataSize++; } + break; + } + + case 8: + { + if(appDev->dataSize > 0){ + appDev->dataSize--; + appDev->dataBuffer[appDev->dataSize] = (uint32_t)0x00;} + break; + } + + case 9: + { + //todo: input + scene_manager_next_scene(appDev->scene_manager, brainfuckSceneSetInput); + break; + } + + case 10: + { + execCountdown = 3; + break; + } + + case 11: + { + //remove old file + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_remove(storage, furi_string_get_cstr(appDev->BF_file_path)); + + //save new file + Stream* stream = buffered_file_stream_alloc(storage); + buffered_file_stream_open(stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS); + stream_write(stream, (const uint8_t*)appDev->dataBuffer, appDev->dataSize); + buffered_file_stream_close(stream); + + //notify + saveNotifyCountdown = 3; + break; + } + } + + bool consumed = false; + return consumed; +} + +static void bf_dev_enter_callback(void* context) { + furi_assert(context); + BFDevEnv* devEnv = context; + + with_view_model( + devEnv->view, + BFDevEnvModel* model, + { + model->col = 0; + model->row = 0; + }, + true); + + appDev = devEnv->appDev; + selectedButton = 0; + + //clear the bf instruction buffer + memset(appDev->dataBuffer, 0x00, BF_INST_BUFFER_SIZE * sizeof(char)); + + //open the file + Storage* storage = furi_record_open(RECORD_STORAGE); + Stream* stream = buffered_file_stream_alloc(storage); + buffered_file_stream_open(stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_READ, FSOM_OPEN_EXISTING); + + //read into the buffer + appDev->dataSize = stream_size(stream); + stream_read(stream, (uint8_t*)appDev->dataBuffer, appDev->dataSize); + buffered_file_stream_close(stream); + + //replaces any invalid characters with an underscore. strips out newlines, comments, etc + for(int i = 0; i < appDev->dataSize; i++){ + if(!strchr(bfChars, appDev->dataBuffer[i])){ + appDev->dataBuffer[i] = '_'; + } + } + + //find the end of the file to begin editing + int tptr = 0; + while(appDev->dataBuffer[tptr] != 0x00){ tptr++; } + appDev->dataSize = tptr; +} + +BFDevEnv* bf_dev_env_alloc(BFApp* appDev) { + BFDevEnv* devEnv = malloc(sizeof(BFDevEnv)); + + devEnv->view = view_alloc(); + devEnv->appDev = appDev; + view_allocate_model(devEnv->view, ViewModelTypeLocking, sizeof(BFDevEnvModel)); + + with_view_model( + devEnv->view, + BFDevEnvModel* model, + { + model->col = 0; + model->row = 0; + }, + true); + + view_set_context(devEnv->view, devEnv); + view_set_draw_callback(devEnv->view, bf_dev_draw_callback); + view_set_input_callback(devEnv->view, bf_dev_input_callback); + view_set_enter_callback(devEnv->view, bf_dev_enter_callback); + return devEnv; +} + +void bf_dev_env_free(BFDevEnv* devEnv) { + furi_assert(devEnv); + view_free(devEnv->view); + free(devEnv); +} + +View* bf_dev_env_get_view(BFDevEnv* devEnv) { + furi_assert(devEnv); + return devEnv->view; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/views/bf_dev_env.h b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/views/bf_dev_env.h new file mode 100644 index 000000000..ea1f6e41f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/views/bf_dev_env.h @@ -0,0 +1,18 @@ +#pragma once +#include "../brainfuck_i.h" +#include + +typedef void (*DevEnvOkCallback)(InputType type, void* context); + +BFDevEnv* bf_dev_env_alloc(BFApp* application); + +void bf_dev_set_file_path(FuriString* path); + +void bf_dev_env_free(BFDevEnv* devEnv); + +View* bf_dev_env_get_view(BFDevEnv* devEnv); + +void bf_dev_env_set_ok( + BFDevEnv* devEnv, + DevEnvOkCallback callback, + void* context); diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/worker.c b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/worker.c new file mode 100644 index 000000000..db05c3c41 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/worker.c @@ -0,0 +1,207 @@ +#include "worker.h" + +int status = 0; //0: idle, 1: running, 2: failure + +char* inst = 0; +int instCount = 0; +int instPtr = 0; +int runOpCount = 0; + +char* wOutput = 0; +int wOutputPtr = 0; + +char* wInput = 0; +int wInputPtr = 0; + +uint8_t* bfStack = 0; +int stackPtr = 0; +int stackSize = BF_STACK_INITIAL_SIZE; +int stackSizeReal = 0; + +bool validateInstPtr(){ + if(instPtr > instCount || instPtr < 0){ + return false; + } + return true; +} + +bool validateStackPtr(){ + if(stackPtr > stackSize || stackPtr < 0){ + return false; + } + return true; +} + +char* workerGetOutput(){ + return wOutput; +} + +int getStackSize(){ + return stackSizeReal; +} + +int getOpCount(){ + return runOpCount; +} + +int getStatus(){ + return status; +} + +void initWorker(BFApp* app){ + //rebuild output + if(wOutput){ free(wOutput); } + wOutput = (char*)malloc(BF_OUTPUT_SIZE); + wOutputPtr = 0; + + //rebuild stack + if(bfStack){ free(bfStack); } + bfStack = (uint8_t*)malloc(BF_STACK_INITIAL_SIZE); + memset(bfStack, 0x00, BF_STACK_INITIAL_SIZE); + stackSize = BF_STACK_INITIAL_SIZE; + stackSizeReal = 0; + stackPtr = 0; + + //set instructions + inst = app->dataBuffer; + instCount = app->dataSize; + instPtr = 0; + runOpCount = 0; + + //set input + wInput = app->inputBuffer; + wInputPtr = 0; + + //set status + status = 0; +} + +void rShift(){ + runOpCount++; + stackPtr++; + if(!validateStackPtr()){ status = 2; return; } + + while(stackPtr > stackSize){ + stackSize += BF_STACK_STEP_SIZE; + void* tmp = realloc(bfStack, stackSize); + + if(!tmp){ + status = 2; + return; + } + + memset((tmp + stackSize) - BF_STACK_STEP_SIZE, 0x00, BF_STACK_STEP_SIZE); + bfStack = (uint8_t*)tmp; + }; + if(stackPtr > stackSizeReal){ + stackSizeReal = stackPtr; + } +} + +void lShift(){ + runOpCount++; + stackPtr--; + if(!validateStackPtr()){ status = 2; return; } +} + +void inc(){ + runOpCount++; + if(!validateStackPtr()){ status = 2; return; } + bfStack[stackPtr]++; +} + +void dec(){ + runOpCount++; + if(!validateStackPtr()){ status = 2; return; } + bfStack[stackPtr]--; +} + +void print(){ + runOpCount++; + wOutput[wOutputPtr] = bfStack[stackPtr]; + wOutputPtr++; + if(wOutputPtr > (BF_OUTPUT_SIZE - 1)){ wOutputPtr = 0;} +} + +void input(){ + runOpCount++; + + bfStack[stackPtr] = (uint8_t)wInput[wInputPtr]; + if(wInput[wInputPtr] == 0x00 || wInputPtr >= 64){ + wInputPtr = 0; + } + else{ + wInputPtr++; + } +} + +void loop() { + runOpCount++; + if (bfStack[stackPtr] == 0) { + int loopCount = 1; + while (loopCount > 0) { + instPtr++; + if(!validateInstPtr()){ status = 2; return; } + if (inst[instPtr] == '[') { loopCount++; } + else if (inst[instPtr] == ']') { loopCount--; } + } + } +} + +void endLoop() { + runOpCount++; + if (bfStack[stackPtr] != 0) { + int loopCount = 1; + while (loopCount > 0) { + instPtr--; + if(!validateInstPtr()){ status = 2; return; } + if (inst[instPtr] == ']') { loopCount++; } + else if (inst[instPtr] == '[') { loopCount--; } + } + } +} + +void beginWorker(){ + status = 1; + while (inst[instPtr] != 0x00) { + if(status == 2){ return; } + switch (inst[instPtr]) { + case '>': + rShift(); + break; + case '<': + lShift(); + break; + + case '+': + inc(); + break; + + case '-': + dec(); + break; + + case '.': + print(); + break; + + case ',': + input(); + break; + + case '[': + loop(); + break; + + case ']': + endLoop(); + break; + + default: + break; + } + instPtr++; + if(!validateInstPtr()){ status = 2; return; } + } + status = 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/brainfuck/worker.h b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/worker.h new file mode 100644 index 000000000..e0b79d5c6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/brainfuck/worker.h @@ -0,0 +1,8 @@ +#include "brainfuck_i.h" + +void initWorker(BFApp* application); +char* workerGetOutput(); +int getStackSize(); +int getOpCount(); +int getStatus(); +void beginWorker(); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/README.md b/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/README.md new file mode 100644 index 000000000..60b8258f3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/README.md @@ -0,0 +1 @@ +Flipper Zero BT Serial Example App diff --git a/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/application.fam b/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/application.fam new file mode 100644 index 000000000..bd3b10046 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/application.fam @@ -0,0 +1,13 @@ +App( + appid="fbs", + name="BT Serial App", + apptype=FlipperAppType.EXTERNAL, + entry_point="fbs_app", + stack_size=1 * 1024, + requires=[ + "bt", + "gui", + ], + fap_category="Misc_Extra", + fap_icon="uart_10px.png", +) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/fbs.c b/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/fbs.c new file mode 100644 index 000000000..e548351d7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/fbs.c @@ -0,0 +1,108 @@ +#include "fbs.h" + +const uint16_t BT_SERIAL_BUFFER_SIZE = 128; + +void draw_callback(Canvas* canvas, void* ctx) { + FBS* app = ctx; + furi_check(furi_mutex_acquire(app->app_mutex, FuriWaitForever) == FuriStatusOk); + + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, (char*)app->display_text); + + furi_mutex_release(app->app_mutex); +} + +void input_callback(InputEvent* input, void* ctx) { + FBS* app = ctx; + furi_message_queue_put(app->event_queue, input, FuriWaitForever); +} + +FBS* fbs_alloc() { + FBS* app = malloc(sizeof(FBS)); + app->app_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + app->gui = furi_record_open(RECORD_GUI); + app->view_port = view_port_alloc(); + view_port_draw_callback_set(app->view_port, draw_callback, app); + view_port_input_callback_set(app->view_port, input_callback, app); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + + app->bt_connected = false; + app->bt = furi_record_open(RECORD_BT); + return app; +} + +void fbs_free(FBS* app) { + view_port_enabled_set(app->view_port, false); + gui_remove_view_port(app->gui, app->view_port); + furi_record_close(RECORD_GUI); + app->gui = NULL; + view_port_free(app->view_port); + free(app->display_text); + + furi_mutex_free(app->app_mutex); + furi_message_queue_free(app->event_queue); + + furi_record_close(RECORD_BT); + app->bt = NULL; + + free(app); +} + +static uint16_t bt_serial_event_callback(SerialServiceEvent event, void* context) { + furi_assert(context); + Bt* bt = context; + UNUSED(bt); + uint16_t ret = 0; + + if(event.event == SerialServiceEventTypeDataReceived) { + FURI_LOG_D(TAG, "SerialServiceEventTypeDataReceived"); + FURI_LOG_D(TAG, "Size: %u", event.data.size); + FURI_LOG_D(TAG, "Data: "); + for (size_t i = 0; i < event.data.size; i++) + { + printf("%X ", event.data.buffer[i]); + } + printf("\r\n"); + } else if(event.event == SerialServiceEventTypeDataSent) { + FURI_LOG_D(TAG, "SerialServiceEventTypeDataSent"); + FURI_LOG_D(TAG, "Size: %u", event.data.size); + FURI_LOG_D(TAG, "Data: "); + for (size_t i = 0; i < event.data.size; i++) + { + printf("%X ", event.data.buffer[i]); + } + printf("\r\n"); + } + return ret; +} + +int32_t fbs_app(void* p) { + UNUSED(p); + FBS* app = fbs_alloc(); + + if (furi_hal_bt_is_active()) { + FURI_LOG_D(TAG, "BT is working, hijacking the serial connection..."); + furi_hal_bt_serial_set_event_callback(BT_SERIAL_BUFFER_SIZE, bt_serial_event_callback, app); + furi_hal_bt_start_advertising(); + } else { + FURI_LOG_D(TAG, "Please, enable the Bluetooth and restart the app"); + } + + InputEvent event; + for(bool processing = true; processing;) { + int status = furi_message_queue_get(app->event_queue, &event, 100); + furi_check(furi_mutex_acquire(app->app_mutex, FuriWaitForever) == FuriStatusOk); + if(status == FuriStatusOk && event.type == InputTypePress && event.key == InputKeyBack) { + processing = false; + } + furi_mutex_release(app->app_mutex); + view_port_update(app->view_port); + } + + furi_hal_bt_serial_set_event_callback(0, NULL, NULL); + + fbs_free(app); + FURI_LOG_D(TAG, "Released everything"); + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/fbs.h b/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/fbs.h new file mode 100644 index 000000000..325936be2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/fbs.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#define TAG "FlipperBTSerial" + +typedef struct { + Bt* bt; + bool bt_connected; + + char* display_text; + + ViewPort* view_port; + Gui* gui; + + FuriMutex* app_mutex; + FuriMessageQueue* event_queue; +} FBS; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/uart_10px.png b/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/uart_10px.png new file mode 100644 index 000000000..8420f5692 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/bt_serial_example/uart_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/README.md b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/README.md new file mode 100644 index 000000000..0d434f9de --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/README.md @@ -0,0 +1,17 @@ +# Caesar Cipher + +A [caesar cipher](https://en.wikipedia.org/wiki/Caesar_cipher) encoder for the Flipper Zero device. + +![input](img/1.png) +![output](img/2.png) + +## Usage + +Start app, painfully input your ciphertext with the onscreen keyboard. Replace spaces with underscores. Hit "Save", scroll output. + +## Compiling + +``` +./fbt firmware_caesar_cipher +``` + diff --git a/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/application.fam b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/application.fam new file mode 100644 index 000000000..30b12a636 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/application.fam @@ -0,0 +1,14 @@ +App( + appid="Caesar_Cipher", + name="Caesar Cipher", + apptype=FlipperAppType.PLUGIN, + entry_point="caesar_cipher_app", + cdefines=["APP_CAESAR_CIPHER"], + requires=[ + "gui", + ], + stack_size=2 * 1024, + fap_icon="caesar_cipher_icon.png", + fap_category="Misc_Extra", + order=20, +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/caesar_cipher.c b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/caesar_cipher.c new file mode 100644 index 000000000..9eb93e925 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/caesar_cipher.c @@ -0,0 +1,147 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#define TEXT_BUFFER_SIZE 256 + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + ViewDispatcher* view_dispatcher; + TextInput* text_input; + TextBox* text_box; + char input[TEXT_BUFFER_SIZE]; + char output[(TEXT_BUFFER_SIZE * 26) + (26)]; // linebreaks +} CaesarState; + +static void string_to_uppercase(char* input) { + int i; + for(i = 0; input[i] != '\0'; i++) { + if(input[i] >= 'a' && input[i] <= 'z') { + input[i] = input[i] - 32; + } else { + input[i] = input[i]; + } + } +} + +static void build_output(char* input, char* output) { + int out = 0; + for(int rot = 1; rot < 26; rot++) { + int in; + for(in = 0; input[in] != '\0'; in++) { + if(input[in] >= 'A' && input[in] <= 'Z') { + output[out] = 65 + (((input[in] - 65) + rot) % 26); + } else { + output[out] = input[in]; + } + out++; + } + output[out] = '\n'; + out++; + } + output[out] = '\0'; +} + +static void text_input_callback(void* ctx) { + CaesarState* caesar_state = acquire_mutex((ValueMutex*)ctx, 25); + FURI_LOG_D("caesar_cipher", "Input text: %s", caesar_state->input); + // this is where we build the output. + string_to_uppercase(caesar_state->input); + FURI_LOG_D("caesar_cipher", "Upper text: %s", caesar_state->input); + build_output(caesar_state->input, caesar_state->output); + text_box_set_text(caesar_state->text_box, caesar_state->output); + view_dispatcher_switch_to_view(caesar_state->view_dispatcher, 1); + + release_mutex((ValueMutex*)ctx, caesar_state); +} + +static bool back_event_callback(void* ctx) { + const CaesarState* caesar_state = acquire_mutex((ValueMutex*)ctx, 25); + view_dispatcher_stop(caesar_state->view_dispatcher); + release_mutex((ValueMutex*)ctx, caesar_state); + return true; +} + +static void caesar_cipher_state_init(CaesarState* const caesar_state) { + caesar_state->view_dispatcher = view_dispatcher_alloc(); + caesar_state->text_input = text_input_alloc(); + caesar_state->text_box = text_box_alloc(); + text_box_set_font(caesar_state->text_box, TextBoxFontText); +} + +static void caesar_cipher_state_free(CaesarState* const caesar_state) { + text_input_free(caesar_state->text_input); + text_box_free(caesar_state->text_box); + view_dispatcher_remove_view(caesar_state->view_dispatcher, 0); + view_dispatcher_remove_view(caesar_state->view_dispatcher, 1); + view_dispatcher_free(caesar_state->view_dispatcher); + free(caesar_state); +} + +int32_t caesar_cipher_app() { + CaesarState* caesar_state = malloc(sizeof(CaesarState)); + + FURI_LOG_D("caesar_cipher", "Running caesar_cipher_state_init"); + caesar_cipher_state_init(caesar_state); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, caesar_state, sizeof(CaesarState))) { + FURI_LOG_E("caesar_cipher", "cannot create mutex\r\n"); + free(caesar_state); + return 255; + } + + FURI_LOG_D("caesar_cipher", "Assigning text input callback"); + text_input_set_result_callback( + caesar_state->text_input, + text_input_callback, + &state_mutex, + caesar_state->input, + TEXT_BUFFER_SIZE, + //clear default text + true); + text_input_set_header_text(caesar_state->text_input, "Input"); + + // Open GUI and register view_port + Gui* gui = furi_record_open("gui"); + //gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + FURI_LOG_D("caesar_cipher", "Enabling view dispatcher queue"); + view_dispatcher_enable_queue(caesar_state->view_dispatcher); + + FURI_LOG_D("caesar_cipher", "Adding text input view to dispatcher"); + view_dispatcher_add_view( + caesar_state->view_dispatcher, 0, text_input_get_view(caesar_state->text_input)); + view_dispatcher_add_view( + caesar_state->view_dispatcher, 1, text_box_get_view(caesar_state->text_box)); + FURI_LOG_D("caesar_cipher", "Attaching view dispatcher to GUI"); + view_dispatcher_attach_to_gui( + caesar_state->view_dispatcher, gui, ViewDispatcherTypeFullscreen); + FURI_LOG_D("ceasar_cipher", "starting view dispatcher"); + view_dispatcher_set_navigation_event_callback( + caesar_state->view_dispatcher, back_event_callback); + view_dispatcher_set_event_callback_context(caesar_state->view_dispatcher, &state_mutex); + view_dispatcher_switch_to_view(caesar_state->view_dispatcher, 0); + view_dispatcher_run(caesar_state->view_dispatcher); + + furi_record_close("gui"); + delete_mutex(&state_mutex); + caesar_cipher_state_free(caesar_state); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/caesar_cipher_icon.png b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/caesar_cipher_icon.png new file mode 100644 index 000000000..13077e892 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/caesar_cipher_icon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/img/1.png b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/img/1.png new file mode 100644 index 000000000..93a9bcdbe Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/img/1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/img/2.png b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/img/2.png new file mode 100644 index 000000000..99d1bcc63 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/caesarcipher/img/2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/calculator/application.fam b/Applications/Official/DEV_FW/source/xMasterX/calculator/application.fam new file mode 100644 index 000000000..33f1d6cf8 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/calculator/application.fam @@ -0,0 +1,12 @@ +App( + appid="Calculator", + name="Calculator", + apptype=FlipperAppType.EXTERNAL, + entry_point="calculator_app", + cdefines=["APP_CALCULATOR"], + requires=["gui"], + stack_size=1 * 1024, + order=45, + fap_icon="calcIcon.png", + fap_category="Misc_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/calculator/calc.png b/Applications/Official/DEV_FW/source/xMasterX/calculator/calc.png new file mode 100644 index 000000000..838d7b964 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/calculator/calc.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/calculator/calcIcon.png b/Applications/Official/DEV_FW/source/xMasterX/calculator/calcIcon.png new file mode 100644 index 000000000..05cda6ce6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/calculator/calcIcon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/calculator/calculator.c b/Applications/Official/DEV_FW/source/xMasterX/calculator/calculator.c new file mode 100644 index 000000000..b121641b0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/calculator/calculator.c @@ -0,0 +1,453 @@ +#include +#include +#include +#include +#include +#include +#include // Header-file for boolean data-type. +#include // Header-file for string functions. +#include "tinyexpr.h" // Header-file for the TinyExpr library. + +#include +#include + +const short MAX_TEXT_LENGTH = 20; + +typedef struct { + short x; + short y; +} selectedPosition; + +typedef struct { + selectedPosition position; + //string with the inputted calculator text + char text[20]; + short textLength; + char log[20]; +} Calculator; + +char getKeyAtPosition(short x, short y) { + if(x == 0 && y == 0) { + return 'C'; + } + if(x == 1 && y == 0) { + return '<'; + } + if(x == 2 && y == 0) { + return '%'; + } + if(x == 3 && y == 0) { + return '/'; + } + if(x == 0 && y == 1) { + return '1'; + } + if(x == 1 && y == 1) { + return '2'; + } + if(x == 2 && y == 1) { + return '3'; + } + if(x == 3 && y == 1) { + return '*'; + } + if(x == 0 && y == 2) { + return '4'; + } + if(x == 1 && y == 2) { + return '5'; + } + if(x == 2 && y == 2) { + return '6'; + } + if(x == 3 && y == 2) { + return '-'; + } + if(x == 0 && y == 3) { + return '7'; + } + if(x == 1 && y == 3) { + return '8'; + } + if(x == 2 && y == 3) { + return '9'; + } + if(x == 3 && y == 3) { + return '+'; + } + if(x == 0 && y == 4) { + return '('; + } + if(x == 1 && y == 4) { + return '0'; + } + if(x == 2 && y == 4) { + return '.'; + } + if(x == 3 && y == 4) { + return '='; + } + return ' '; +} + +short calculateStringWidth(const char* str, short lenght) { + /* widths: + 1 = 2 + 2, 3, 4, 5, 6, 7, 8, 9, 0, X, -, +, . = = 5 + %, / = 7 + S = 5 + (, ) = 3 + + */ + short width = 0; + for(short i = 0; i < lenght; i++) { + switch(str[i]) { + case '1': + width += 2; + break; + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '0': + case '*': + case '-': + case '+': + case '.': + width += 5; + break; + case '%': + case '/': + width += 7; + break; + case 'S': + width += 5; + break; + case '(': + case ')': + width += 3; + break; + default: + break; + } + width += 1; + } + + return width; +} + +void generate_calculator_layout(Canvas* canvas) { + //draw dotted lines + for(int i = 0; i <= 64; i++) { + if(i % 2 == 0) { + canvas_draw_dot(canvas, i, 14); + canvas_draw_dot(canvas, i, 33); + } + if(i % 2 == 1) { + canvas_draw_dot(canvas, i, 15); + canvas_draw_dot(canvas, i, 34); + } + } + + //draw horizontal lines + canvas_draw_box(canvas, 0, 41, 64, 2); + canvas_draw_box(canvas, 0, 57, 64, 2); + canvas_draw_box(canvas, 0, 73, 64, 2); + canvas_draw_box(canvas, 0, 89, 64, 2); + canvas_draw_box(canvas, 0, 105, 64, 2); + canvas_draw_box(canvas, 0, 121, 64, 2); + + //draw vertical lines + canvas_draw_box(canvas, 0, 43, 1, 80); + canvas_draw_box(canvas, 15, 43, 2, 80); + canvas_draw_box(canvas, 31, 43, 2, 80); + canvas_draw_box(canvas, 47, 43, 2, 80); + canvas_draw_box(canvas, 63, 43, 1, 80); + + //draw buttons + //row 1 (C, ;, %, ÷) + canvas_draw_str(canvas, 5, 54, "C"); + canvas_draw_str(canvas, 19, 54, " <-"); + canvas_draw_str(canvas, 35, 54, " %"); + canvas_draw_str(canvas, 51, 54, " /"); + + //row 2 (1, 2, 3, X) + canvas_draw_str(canvas, 5, 70, " 1"); + canvas_draw_str(canvas, 19, 70, " 2"); + canvas_draw_str(canvas, 35, 70, " 3"); + canvas_draw_str(canvas, 51, 70, " X"); + + //row 3 (4, 5, 6, -) + canvas_draw_str(canvas, 5, 86, " 4"); + canvas_draw_str(canvas, 19, 86, " 5"); + canvas_draw_str(canvas, 35, 86, " 6"); + canvas_draw_str(canvas, 51, 86, " -"); + + //row 4 (7, 8, 9, +) + canvas_draw_str(canvas, 5, 102, " 7"); + canvas_draw_str(canvas, 19, 102, " 8"); + canvas_draw_str(canvas, 35, 102, " 9"); + canvas_draw_str(canvas, 51, 102, " +"); + + //row 5 (+/-, 0, ., =) + canvas_draw_str(canvas, 3, 118, "( )"); + canvas_draw_str(canvas, 19, 118, " 0"); + canvas_draw_str(canvas, 35, 118, " ."); + canvas_draw_str(canvas, 51, 118, " ="); +}; + +void calculator_draw_callback(Canvas* canvas, void* ctx) { + const Calculator* calculator_state = acquire_mutex((ValueMutex*)ctx, 25); + UNUSED(ctx); + canvas_clear(canvas); + + //show selected button + short startX = 1; + short startY = 43; + + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, + startX + (calculator_state->position.x) * 16, + (startY) + (calculator_state->position.y) * 16, + 16, + 16); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box( + canvas, + startX + (calculator_state->position.x) * 16 + 2, + (startY) + (calculator_state->position.y) * 16 + 2, + 10, + 10); + + canvas_set_color(canvas, ColorBlack); + generate_calculator_layout(canvas); + + //draw text + short stringWidth = calculateStringWidth(calculator_state->text, calculator_state->textLength); + short startingPosition = 5; + if(stringWidth > 60) { + startingPosition += 60 - (stringWidth + 5); + } + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, startingPosition, 28, calculator_state->text); + //canvas_draw_str(canvas, 10, 10, calculator_state->log); + + //draw cursor + canvas_draw_box(canvas, stringWidth + 5, 29, 5, 1); + + release_mutex((ValueMutex*)ctx, calculator_state); +} + +void calculator_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +void calculate(Calculator* calculator_state) { + double result; + result = te_interp(calculator_state->text, 0); + + calculator_state->textLength = 0; + calculator_state->text[0] = '\0'; + // sprintf(calculator_state->text, "%f", result); + + //invert sign if negative + if(result < 0) { + calculator_state->text[calculator_state->textLength++] = '-'; + result = -result; + } + + //get numbers before and after decimal + int beforeDecimal = result; + int afterDecimal = (result - beforeDecimal) * 100; + + char beforeDecimalString[10]; + char afterDecimalString[10]; + int i = 0; + //parse to a string + while(beforeDecimal > 0) { + beforeDecimalString[i++] = beforeDecimal % 10 + '0'; + beforeDecimal /= 10; + } + // invert string + for(int j = 0; j < i / 2; j++) { + char temp = beforeDecimalString[j]; + beforeDecimalString[j] = beforeDecimalString[i - j - 1]; + beforeDecimalString[i - j - 1] = temp; + } + //add it to the answer + for(int j = 0; j < i; j++) { + calculator_state->text[calculator_state->textLength++] = beforeDecimalString[j]; + } + + i = 0; + if(afterDecimal > 0) { + while(afterDecimal > 0) { + afterDecimalString[i++] = afterDecimal % 10 + '0'; + afterDecimal /= 10; + } + // invert string + for(int j = 0; j < i / 2; j++) { + char temp = afterDecimalString[j]; + afterDecimalString[j] = afterDecimalString[i - j - 1]; + afterDecimalString[i - j - 1] = temp; + } + + //add decimal point + calculator_state->text[calculator_state->textLength++] = '.'; + + //add numbers after decimal + for(int j = 0; j < i; j++) { + calculator_state->text[calculator_state->textLength++] = afterDecimalString[j]; + } + } + calculator_state->text[calculator_state->textLength] = '\0'; +} + +int32_t calculator_app(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + Calculator* calculator_state = malloc(sizeof(Calculator)); + ValueMutex calculator_state_mutex; + if(!init_mutex(&calculator_state_mutex, calculator_state, sizeof(Calculator))) { + //FURI_LOG_E("calculator", "cannot create mutex\r\n"); + free(calculator_state); + return -1; + } + + // Configure view port + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, calculator_draw_callback, &calculator_state_mutex); + view_port_input_callback_set(view_port, calculator_input_callback, event_queue); + view_port_set_orientation(view_port, ViewPortOrientationVertical); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + //NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + InputEvent event; + + while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { + //break out of the loop if the back key is pressed + if(event.type == InputTypeShort && event.key == InputKeyBack) { + break; + } + + if(event.type == InputTypeShort) { + switch(event.key) { + case InputKeyUp: + if(calculator_state->position.y > 0) { + calculator_state->position.y--; + } + break; + case InputKeyDown: + if(calculator_state->position.y < 4) { + calculator_state->position.y++; + } + break; + case InputKeyLeft: + if(calculator_state->position.x > 0) { + calculator_state->position.x--; + } + break; + case InputKeyRight: + if(calculator_state->position.x < 3) { + calculator_state->position.x++; + } + break; + case InputKeyOk: { + //add the selected button to the text + //char* text = calculator_state->text; + // short* textLength = &calculator_state->textLength; + + char key = + getKeyAtPosition(calculator_state->position.x, calculator_state->position.y); + + switch(key) { + case 'C': + while(calculator_state->textLength > 0) { + calculator_state->text[calculator_state->textLength--] = '\0'; + } + calculator_state->text[0] = '\0'; + calculator_state->log[2] = key; + break; + case '<': + calculator_state->log[2] = key; + if(calculator_state->textLength > 0) { + calculator_state->text[--calculator_state->textLength] = '\0'; + } else { + calculator_state->text[0] = '\0'; + } + break; + case '=': + calculator_state->log[2] = key; + calculate(calculator_state); + break; + case '%': + case '/': + case '*': + case '-': + case '+': + case '.': + case '(': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '0': + if(calculator_state->textLength < MAX_TEXT_LENGTH) { + calculator_state->text[calculator_state->textLength++] = key; + calculator_state->text[calculator_state->textLength] = '\0'; + } + //calculator_state->log[1] = calculator_state->text[*textLength]; + break; + default: + break; + } + } + default: + break; + } + + view_port_update(view_port); + } + + if(event.type == InputTypeLong) { + switch(event.key) { + case InputKeyOk: + if(calculator_state->position.x == 0 && calculator_state->position.y == 4) { + if(calculator_state->textLength < MAX_TEXT_LENGTH) { + calculator_state->text[calculator_state->textLength++] = ')'; + calculator_state->text[calculator_state->textLength] = '\0'; + } + view_port_update(view_port); + } + break; + default: + break; + } + } + } + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/calculator/tinyexpr.c b/Applications/Official/DEV_FW/source/xMasterX/calculator/tinyexpr.c new file mode 100644 index 000000000..40e25236f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/calculator/tinyexpr.c @@ -0,0 +1,785 @@ +// SPDX-License-Identifier: Zlib +/* + * TINYEXPR - Tiny recursive descent parser and evaluation engine in C + * + * Copyright (c) 2015-2020 Lewis Van Winkle + * + * http://CodePlea.com + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgement in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* COMPILE TIME OPTIONS */ + +/* Exponentiation associativity: +For a^b^c = (a^b)^c and -a^b = (-a)^b do nothing. +For a^b^c = a^(b^c) and -a^b = -(a^b) uncomment the next line.*/ +/* #define TE_POW_FROM_RIGHT */ + +/* Logarithms +For log = base 10 log do nothing +For log = natural log uncomment the next line. */ +/* #define TE_NAT_LOG */ + +#include "tinyexpr.h" +#include +#include +#include +#include +#include +#include + +#ifndef NAN +#define NAN (0.0 / 0.0) +#endif + +#ifndef INFINITY +#define INFINITY (1.0 / 0.0) +#endif + +typedef double (*te_fun2)(double, double); + +enum { + TOK_NULL = TE_CLOSURE7 + 1, + TOK_ERROR, + TOK_END, + TOK_SEP, + TOK_OPEN, + TOK_CLOSE, + TOK_NUMBER, + TOK_VARIABLE, + TOK_INFIX +}; + +enum { TE_CONSTANT = 1 }; + +typedef struct state { + const char* start; + const char* next; + int type; + union { + double value; + const double* bound; + const void* function; + }; + void* context; + + const te_variable* lookup; + int lookup_len; +} state; + +#define TYPE_MASK(TYPE) ((TYPE)&0x0000001F) + +#define IS_PURE(TYPE) (((TYPE)&TE_FLAG_PURE) != 0) +#define IS_FUNCTION(TYPE) (((TYPE)&TE_FUNCTION0) != 0) +#define IS_CLOSURE(TYPE) (((TYPE)&TE_CLOSURE0) != 0) +#define ARITY(TYPE) (((TYPE) & (TE_FUNCTION0 | TE_CLOSURE0)) ? ((TYPE)&0x00000007) : 0) +#define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){__VA_ARGS__}) + +static te_expr* new_expr(const int type, const te_expr* parameters[]) { + const int arity = ARITY(type); + const int psize = sizeof(void*) * arity; + const int size = + (sizeof(te_expr) - sizeof(void*)) + psize + (IS_CLOSURE(type) ? sizeof(void*) : 0); + te_expr* ret = malloc(size); + memset(ret, 0, size); + if(arity && parameters) { + memcpy(ret->parameters, parameters, psize); + } + ret->type = type; + ret->bound = 0; + return ret; +} + +void te_free_parameters(te_expr* n) { + if(!n) return; + switch(TYPE_MASK(n->type)) { + case TE_FUNCTION7: + case TE_CLOSURE7: + te_free(n->parameters[6]); /* Falls through. */ + case TE_FUNCTION6: + case TE_CLOSURE6: + te_free(n->parameters[5]); /* Falls through. */ + case TE_FUNCTION5: + case TE_CLOSURE5: + te_free(n->parameters[4]); /* Falls through. */ + case TE_FUNCTION4: + case TE_CLOSURE4: + te_free(n->parameters[3]); /* Falls through. */ + case TE_FUNCTION3: + case TE_CLOSURE3: + te_free(n->parameters[2]); /* Falls through. */ + case TE_FUNCTION2: + case TE_CLOSURE2: + te_free(n->parameters[1]); /* Falls through. */ + case TE_FUNCTION1: + case TE_CLOSURE1: + te_free(n->parameters[0]); + } +} + +void te_free(te_expr* n) { + if(!n) return; + te_free_parameters(n); + free(n); +} + +static double pi(void) { + return 3.14159265358979323846; +} +static double e(void) { + return 2.71828182845904523536; +} +static double fac(double a) { /* simplest version of fac */ + if(a < 0.0) return NAN; + if(a > UINT_MAX) return INFINITY; + unsigned int ua = (unsigned int)(a); + unsigned long int result = 1, i; + for(i = 1; i <= ua; i++) { + if(i > ULONG_MAX / result) return INFINITY; + result *= i; + } + return (double)result; +} +static double ncr(double n, double r) { + if(n < 0.0 || r < 0.0 || n < r) return NAN; + if(n > UINT_MAX || r > UINT_MAX) return INFINITY; + unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i; + unsigned long int result = 1; + if(ur > un / 2) ur = un - ur; + for(i = 1; i <= ur; i++) { + if(result > ULONG_MAX / (un - ur + i)) return INFINITY; + result *= un - ur + i; + result /= i; + } + return result; +} +static double npr(double n, double r) { + return ncr(n, r) * fac(r); +} + +#ifdef _MSC_VER +#pragma function(ceil) +#pragma function(floor) +#endif + +static const te_variable functions[] = { + /* must be in alphabetical order */ + {"abs", fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"acos", acos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"asin", asin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"atan", atan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"atan2", atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"ceil", ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"cos", cos, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"cosh", cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"e", e, TE_FUNCTION0 | TE_FLAG_PURE, 0}, + {"exp", exp, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"fac", fac, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"ln", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, +#ifdef TE_NAT_LOG + {"log", log, TE_FUNCTION1 | TE_FLAG_PURE, 0}, +#else + {"log", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, +#endif + {"log10", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"ncr", ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"npr", npr, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"pi", pi, TE_FUNCTION0 | TE_FLAG_PURE, 0}, + {"pow", pow, TE_FUNCTION2 | TE_FLAG_PURE, 0}, + {"sin", sin, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"sinh", sinh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"sqrt", sqrt, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"tan", tan, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {"tanh", tanh, TE_FUNCTION1 | TE_FLAG_PURE, 0}, + {0, 0, 0, 0}}; + +static const te_variable* find_builtin(const char* name, int len) { + int imin = 0; + int imax = sizeof(functions) / sizeof(te_variable) - 2; + + /*Binary search.*/ + while(imax >= imin) { + const int i = (imin + ((imax - imin) / 2)); + int c = strncmp(name, functions[i].name, len); + if(!c) c = '\0' - functions[i].name[len]; + if(c == 0) { + return functions + i; + } else if(c > 0) { + imin = i + 1; + } else { + imax = i - 1; + } + } + + return 0; +} + +static const te_variable* find_lookup(const state* s, const char* name, int len) { + int iters; + const te_variable* var; + if(!s->lookup) return 0; + + for(var = s->lookup, iters = s->lookup_len; iters; ++var, --iters) { + if(strncmp(name, var->name, len) == 0 && var->name[len] == '\0') { + return var; + } + } + return 0; +} + +static double add(double a, double b) { + return a + b; +} +static double sub(double a, double b) { + return a - b; +} +static double mul(double a, double b) { + return a * b; +} +static double divide(double a, double b) { + return a / b; +} +static double negate(double a) { + return -a; +} +static double comma(double a, double b) { + (void)a; + return b; +} + +void next_token(state* s) { + s->type = TOK_NULL; + + do { + if(!*s->next) { + s->type = TOK_END; + return; + } + + /* Try reading a number. */ + if((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') { + s->value = strtof(s->next, (char**)&s->next); + s->type = TOK_NUMBER; + } else { + /* Look for a variable or builtin function call. */ + if(isalpha(s->next[0])) { + const char* start; + start = s->next; + while(isalpha(s->next[0]) || isdigit(s->next[0]) || (s->next[0] == '_')) s->next++; + + const te_variable* var = find_lookup(s, start, s->next - start); + if(!var) var = find_builtin(start, s->next - start); + + if(!var) { + s->type = TOK_ERROR; + } else { + switch(TYPE_MASK(var->type)) { + case TE_VARIABLE: + s->type = TOK_VARIABLE; + s->bound = var->address; + break; + + case TE_CLOSURE0: + case TE_CLOSURE1: + case TE_CLOSURE2: + case TE_CLOSURE3: /* Falls through. */ + case TE_CLOSURE4: + case TE_CLOSURE5: + case TE_CLOSURE6: + case TE_CLOSURE7: /* Falls through. */ + s->context = var->context; /* Falls through. */ + + case TE_FUNCTION0: + case TE_FUNCTION1: + case TE_FUNCTION2: + case TE_FUNCTION3: /* Falls through. */ + case TE_FUNCTION4: + case TE_FUNCTION5: + case TE_FUNCTION6: + case TE_FUNCTION7: /* Falls through. */ + s->type = var->type; + s->function = var->address; + break; + } + } + + } else { + /* Look for an operator or special character. */ + switch(s->next++[0]) { + case '+': + s->type = TOK_INFIX; + s->function = add; + break; + case '-': + s->type = TOK_INFIX; + s->function = sub; + break; + case '*': + s->type = TOK_INFIX; + s->function = mul; + break; + case '/': + s->type = TOK_INFIX; + s->function = divide; + break; + case '^': + s->type = TOK_INFIX; + s->function = pow; + break; + case '%': + s->type = TOK_INFIX; + s->function = fmod; + break; + case '(': + s->type = TOK_OPEN; + break; + case ')': + s->type = TOK_CLOSE; + break; + case ',': + s->type = TOK_SEP; + break; + case ' ': + case '\t': + case '\n': + case '\r': + break; + default: + s->type = TOK_ERROR; + break; + } + } + } + } while(s->type == TOK_NULL); +} + +static te_expr* list(state* s); +static te_expr* expr(state* s); +static te_expr* power(state* s); + +static te_expr* base(state* s) { + /* = | | {"(" ")"} | | "(" {"," } ")" | "(" ")" */ + te_expr* ret; + int arity; + + switch(TYPE_MASK(s->type)) { + case TOK_NUMBER: + ret = new_expr(TE_CONSTANT, 0); + ret->value = s->value; + next_token(s); + break; + + case TOK_VARIABLE: + ret = new_expr(TE_VARIABLE, 0); + ret->bound = s->bound; + next_token(s); + break; + + case TE_FUNCTION0: + case TE_CLOSURE0: + ret = new_expr(s->type, 0); + ret->function = s->function; + if(IS_CLOSURE(s->type)) ret->parameters[0] = s->context; + next_token(s); + if(s->type == TOK_OPEN) { + next_token(s); + if(s->type != TOK_CLOSE) { + s->type = TOK_ERROR; + } else { + next_token(s); + } + } + break; + + case TE_FUNCTION1: + case TE_CLOSURE1: + ret = new_expr(s->type, 0); + ret->function = s->function; + if(IS_CLOSURE(s->type)) ret->parameters[1] = s->context; + next_token(s); + ret->parameters[0] = power(s); + break; + + case TE_FUNCTION2: + case TE_FUNCTION3: + case TE_FUNCTION4: + case TE_FUNCTION5: + case TE_FUNCTION6: + case TE_FUNCTION7: + case TE_CLOSURE2: + case TE_CLOSURE3: + case TE_CLOSURE4: + case TE_CLOSURE5: + case TE_CLOSURE6: + case TE_CLOSURE7: + arity = ARITY(s->type); + + ret = new_expr(s->type, 0); + ret->function = s->function; + if(IS_CLOSURE(s->type)) ret->parameters[arity] = s->context; + next_token(s); + + if(s->type != TOK_OPEN) { + s->type = TOK_ERROR; + } else { + int i; + for(i = 0; i < arity; i++) { + next_token(s); + ret->parameters[i] = expr(s); + if(s->type != TOK_SEP) { + break; + } + } + if(s->type != TOK_CLOSE || i != arity - 1) { + s->type = TOK_ERROR; + } else { + next_token(s); + } + } + + break; + + case TOK_OPEN: + next_token(s); + ret = list(s); + if(s->type != TOK_CLOSE) { + s->type = TOK_ERROR; + } else { + next_token(s); + } + break; + + default: + ret = new_expr(0, 0); + s->type = TOK_ERROR; + ret->value = NAN; + break; + } + + return ret; +} + +static te_expr* power(state* s) { + /* = {("-" | "+")} */ + int sign = 1; + while(s->type == TOK_INFIX && (s->function == add || s->function == sub)) { + if(s->function == sub) sign = -sign; + next_token(s); + } + + te_expr* ret; + + if(sign == 1) { + ret = base(s); + } else { + ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s)); + ret->function = negate; + } + + return ret; +} + +#ifdef TE_POW_FROM_RIGHT +static te_expr* factor(state* s) { + /* = {"^" } */ + te_expr* ret = power(s); + + int neg = 0; + + if(ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) { + te_expr* se = ret->parameters[0]; + free(ret); + ret = se; + neg = 1; + } + + te_expr* insertion = 0; + + while(s->type == TOK_INFIX && (s->function == pow)) { + te_fun2 t = s->function; + next_token(s); + + if(insertion) { + /* Make exponentiation go right-to-left. */ + te_expr* insert = + NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], power(s)); + insert->function = t; + insertion->parameters[1] = insert; + insertion = insert; + } else { + ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); + ret->function = t; + insertion = ret; + } + } + + if(neg) { + ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret); + ret->function = negate; + } + + return ret; +} +#else +static te_expr* factor(state* s) { + /* = {"^" } */ + te_expr* ret = power(s); + + while(s->type == TOK_INFIX && (s->function == pow)) { + te_fun2 t = s->function; + next_token(s); + ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s)); + ret->function = t; + } + + return ret; +} +#endif + +static te_expr* term(state* s) { + /* = {("*" | "/" | "%") } */ + te_expr* ret = factor(s); + + while(s->type == TOK_INFIX && + (s->function == mul || s->function == divide || s->function == fmod)) { + te_fun2 t = s->function; + next_token(s); + ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, factor(s)); + ret->function = t; + } + + return ret; +} + +static te_expr* expr(state* s) { + /* = {("+" | "-") } */ + te_expr* ret = term(s); + + while(s->type == TOK_INFIX && (s->function == add || s->function == sub)) { + te_fun2 t = s->function; + next_token(s); + ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, term(s)); + ret->function = t; + } + + return ret; +} + +static te_expr* list(state* s) { + /* = {"," } */ + te_expr* ret = expr(s); + + while(s->type == TOK_SEP) { + next_token(s); + ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, expr(s)); + ret->function = comma; + } + + return ret; +} + +#define TE_FUN(...) ((double (*)(__VA_ARGS__))n->function) +#define M(e) te_eval(n->parameters[e]) + +double te_eval(const te_expr* n) { + if(!n) return NAN; + + switch(TYPE_MASK(n->type)) { + case TE_CONSTANT: + return n->value; + case TE_VARIABLE: + return *n->bound; + + case TE_FUNCTION0: + case TE_FUNCTION1: + case TE_FUNCTION2: + case TE_FUNCTION3: + case TE_FUNCTION4: + case TE_FUNCTION5: + case TE_FUNCTION6: + case TE_FUNCTION7: + switch(ARITY(n->type)) { + case 0: + return TE_FUN(void)(); + case 1: + return TE_FUN(double)(M(0)); + case 2: + return TE_FUN(double, double)(M(0), M(1)); + case 3: + return TE_FUN(double, double, double)(M(0), M(1), M(2)); + case 4: + return TE_FUN(double, double, double, double)(M(0), M(1), M(2), M(3)); + case 5: + return TE_FUN(double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4)); + case 6: + return TE_FUN(double, double, double, double, double, double)( + M(0), M(1), M(2), M(3), M(4), M(5)); + case 7: + return TE_FUN(double, double, double, double, double, double, double)( + M(0), M(1), M(2), M(3), M(4), M(5), M(6)); + default: + return NAN; + } + + case TE_CLOSURE0: + case TE_CLOSURE1: + case TE_CLOSURE2: + case TE_CLOSURE3: + case TE_CLOSURE4: + case TE_CLOSURE5: + case TE_CLOSURE6: + case TE_CLOSURE7: + switch(ARITY(n->type)) { + case 0: + return TE_FUN(void*)(n->parameters[0]); + case 1: + return TE_FUN(void*, double)(n->parameters[1], M(0)); + case 2: + return TE_FUN(void*, double, double)(n->parameters[2], M(0), M(1)); + case 3: + return TE_FUN(void*, double, double, double)(n->parameters[3], M(0), M(1), M(2)); + case 4: + return TE_FUN(void*, double, double, double, double)( + n->parameters[4], M(0), M(1), M(2), M(3)); + case 5: + return TE_FUN(void*, double, double, double, double, double)( + n->parameters[5], M(0), M(1), M(2), M(3), M(4)); + case 6: + return TE_FUN(void*, double, double, double, double, double, double)( + n->parameters[6], M(0), M(1), M(2), M(3), M(4), M(5)); + case 7: + return TE_FUN(void*, double, double, double, double, double, double, double)( + n->parameters[7], M(0), M(1), M(2), M(3), M(4), M(5), M(6)); + default: + return NAN; + } + + default: + return NAN; + } +} + +#undef TE_FUN +#undef M + +static void optimize(te_expr* n) { + /* Evaluates as much as possible. */ + if(n->type == TE_CONSTANT) return; + if(n->type == TE_VARIABLE) return; + + /* Only optimize out functions flagged as pure. */ + if(IS_PURE(n->type)) { + const int arity = ARITY(n->type); + int known = 1; + int i; + for(i = 0; i < arity; ++i) { + optimize(n->parameters[i]); + if(((te_expr*)(n->parameters[i]))->type != TE_CONSTANT) { + known = 0; + } + } + if(known) { + const double value = te_eval(n); + te_free_parameters(n); + n->type = TE_CONSTANT; + n->value = value; + } + } +} + +te_expr* + te_compile(const char* expression, const te_variable* variables, int var_count, int* error) { + state s; + s.start = s.next = expression; + s.lookup = variables; + s.lookup_len = var_count; + + next_token(&s); + te_expr* root = list(&s); + + if(s.type != TOK_END) { + te_free(root); + if(error) { + *error = (s.next - s.start); + if(*error == 0) *error = 1; + } + return 0; + } else { + optimize(root); + if(error) *error = 0; + return root; + } +} + +double te_interp(const char* expression, int* error) { + te_expr* n = te_compile(expression, 0, 0, error); + double ret; + if(n) { + ret = te_eval(n); + te_free(n); + } else { + ret = NAN; + } + return ret; +} + +static void pn(const te_expr* n, int depth) { + int i, arity; + printf("%*s", depth, ""); + + switch(TYPE_MASK(n->type)) { + case TE_CONSTANT: + printf("%f\n", n->value); + break; + case TE_VARIABLE: + printf("bound %p\n", n->bound); + break; + + case TE_FUNCTION0: + case TE_FUNCTION1: + case TE_FUNCTION2: + case TE_FUNCTION3: + case TE_FUNCTION4: + case TE_FUNCTION5: + case TE_FUNCTION6: + case TE_FUNCTION7: + case TE_CLOSURE0: + case TE_CLOSURE1: + case TE_CLOSURE2: + case TE_CLOSURE3: + case TE_CLOSURE4: + case TE_CLOSURE5: + case TE_CLOSURE6: + case TE_CLOSURE7: + arity = ARITY(n->type); + printf("f%d", arity); + for(i = 0; i < arity; i++) { + printf(" %p", n->parameters[i]); + } + printf("\n"); + for(i = 0; i < arity; i++) { + pn(n->parameters[i], depth + 1); + } + break; + } +} + +void te_print(const te_expr* n) { + pn(n, 0); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/calculator/tinyexpr.h b/Applications/Official/DEV_FW/source/xMasterX/calculator/tinyexpr.h new file mode 100644 index 000000000..3833965a1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/calculator/tinyexpr.h @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: Zlib +/* + * TINYEXPR - Tiny recursive descent parser and evaluation engine in C + * + * Copyright (c) 2015-2020 Lewis Van Winkle + * + * http://CodePlea.com + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgement in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef TINYEXPR_H +#define TINYEXPR_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct te_expr { + int type; + union { + double value; + const double* bound; + const void* function; + }; + void* parameters[1]; +} te_expr; + +enum { + TE_VARIABLE = 0, + + TE_FUNCTION0 = 8, + TE_FUNCTION1, + TE_FUNCTION2, + TE_FUNCTION3, + TE_FUNCTION4, + TE_FUNCTION5, + TE_FUNCTION6, + TE_FUNCTION7, + + TE_CLOSURE0 = 16, + TE_CLOSURE1, + TE_CLOSURE2, + TE_CLOSURE3, + TE_CLOSURE4, + TE_CLOSURE5, + TE_CLOSURE6, + TE_CLOSURE7, + + TE_FLAG_PURE = 32 +}; + +typedef struct te_variable { + const char* name; + const void* address; + int type; + void* context; +} te_variable; + +/* Parses the input expression, evaluates it, and frees it. */ +/* Returns NaN on error. */ +double te_interp(const char* expression, int* error); + +/* Parses the input expression and binds variables. */ +/* Returns NULL on error. */ +te_expr* + te_compile(const char* expression, const te_variable* variables, int var_count, int* error); + +/* Evaluates the expression. */ +double te_eval(const te_expr* n); + +/* Prints debugging information on the syntax tree. */ +void te_print(const te_expr* n); + +/* Frees the expression. */ +/* This is safe to call on NULL pointers. */ +void te_free(te_expr* n); + +#ifdef __cplusplus +} +#endif + +#endif /*TINYEXPR_H*/ diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/.gitignore b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/.gitignore new file mode 100644 index 000000000..600d2d33b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/.gitignore @@ -0,0 +1 @@ +.vscode \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/README.md b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/README.md new file mode 100644 index 000000000..b6d35fa7f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/README.md @@ -0,0 +1,24 @@ +# flipperzero-cli-bridge +Allows CLI control from GUI, giving untethered access to sub-ghz chat, system diagnostics, and more. +# Installation +## Easy way - get a .fap file from the releases page +Swing by the [releases](https://github.com/ranchordo/flipperzero-cli-bridge/releases) page, and download a pre-built .fap file for the latest flipperzero firmware. Use [qFlipper](https://flipperzero.one/update) to copy the .fap file into SD Card/apps/Tools/. MAKE SURE TO UPGRADE FLIPPERZERO FIRMWARE TO NEWEST VERSION BEFORE INSTALLING. +## Hard way - building from source +The following commands will (probably) not work on Windows. If you run Windows, use wsl or a linux vm or something. +```sh +git clone https://github.com/flipperdevices/flipperzero-firmware +cd ./flipperzero-firmware +git clone https://github.com/ranchordo/flipperzero-cli-bridge ./applications_user/flipperzero-cli-bridge/ +./fbt fap_dist APPSRC=applications_user/flipperzero-cli-bridge +# If everything went well, the built .fap file can be found in ./dist/f7-D/apps/apps/Tools/cli_gui.fap +``` +# Usage +On the flipperzero, you should be able to find a new application (CLI-GUI Bridge) under Applications->Tools. Opening it will result in a text prompt - the prompt for the command line. Enter a suitable command (quickly pressing the back button or holding `_` on the keyboard will input a space) such as `subghz chat [freq in hz, e.g. 310000000]`, etc, then navigate to and press the SAVE key. You should then see the command window. Use Up and Down to scroll, and use Left or Center to get back to the text input prompt. A quick tap of the back key while viewing the console output sends a Ctrl-C to the console, and a long press of the left or right keys during text input will navigate back to the console output without executing. +## Exiting the app +Holding and then releasing the back key for at least a second or so (long press) will exit the app normally, meaning that the inner terminal will send Ctrl-C and close. Any sessions will be disconnected. + + +Holding and then releasing the OK key while focusing on the console output for at least a second or so (long press) will exit the app while keeping the terminal open. Terminal output will be cleared the next time you launch the app, but whatever command or session was running previously will be resumed. This is especially handy with subghz chat - exiting the app while keeping the terminal open will not disconnect you from the chat, and the flipper will still vibrate briefly whenever a new message comes in (even if the app is closed). + + +NOTE: USB functionality (qFlipper, normal USB CLI) may not work after running the app (especially after exiting without closing the terminal), simply restart your flipper and all USB functionality will return to normal. diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/application.fam b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/application.fam new file mode 100644 index 000000000..9acc4d2be --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/application.fam @@ -0,0 +1,10 @@ +App( + appid="cli_gui", + name="CLI-GUI Bridge", + apptype=FlipperAppType.EXTERNAL, + entry_point="cligui_main", + requires=["gui","cli"], + stack_size=8 * 1024, + fap_icon="cligui.png", + fap_category="Misc_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cli_control.c b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cli_control.c new file mode 100644 index 000000000..e159d7424 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cli_control.c @@ -0,0 +1,109 @@ +#include "cli_control.h" + +#include +#include +#include +#include "cligui_main_i.h" +#include + +volatile bool gotCallbackSet = false; + +FuriStreamBuffer* tx_stream; +FuriStreamBuffer* rx_stream; +static FuriThread* volatile cliThread = NULL; +static void tx_handler_stdout(const char* buffer, size_t size) { + furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever); +} +static void tx_handler(const uint8_t* buffer, size_t size) { + furi_thread_set_stdout_callback(tx_handler_stdout); + cliThread = furi_thread_get_current(); + furi_stream_buffer_send(tx_stream, buffer, size, FuriWaitForever); +} +static size_t real_rx_handler(uint8_t* buffer, size_t size, uint32_t timeout) { + size_t rx_cnt = 0; + while(size > 0) { + size_t batch_size = size; + if(batch_size > 128) batch_size = 128; + size_t len = furi_stream_buffer_receive(rx_stream, buffer, batch_size, timeout); + if(len == 0) break; + size -= len; + buffer += len; + rx_cnt += len; + } + return rx_cnt; +} + +static CliCommand_internal* getInternalCliCommand(Cli* cli, const char* name) { + FuriString* target_command = furi_string_alloc(); + furi_string_set_str(target_command, name); + CliCommand_internal* command = + CliCommandTree_internal_get(((Cli_internal*)cli)->commands, target_command); + furi_string_free(target_command); + return command; +} + +static void session_init(void) { +} +static void session_deinit(void) { +} +static bool session_connected(void) { + return true; +} +static CliSession session; +void latch_tx_handler() { + Cli* global_cli = furi_record_open(RECORD_CLI); + + CliCommand_internal* help_command = getInternalCliCommand(global_cli, "help"); + cliThread = help_command->context; + + furi_thread_set_stdout_callback(tx_handler_stdout); + if(cliThread != NULL) { + ((FuriThread_internal*)cliThread)->output.write_callback = &tx_handler_stdout; + } + + rx_stream = furi_stream_buffer_alloc(128, 1); + tx_stream = furi_stream_buffer_alloc(128, 1); + + session.tx = &tx_handler; + session.rx = &real_rx_handler; + session.tx_stdout = &tx_handler_stdout; + session.init = &session_init; + session.deinit = &session_deinit; + session.is_connected = &session_connected; + cli_session_close(global_cli); + cli_session_open(global_cli, &session); + // Unlock loader-lock + Loader* loader = furi_record_open(RECORD_LOADER); + Loader_internal* loader_i = (Loader_internal*)loader; + loader_i->lock_count = 0; + furi_record_close(RECORD_CLI); + furi_record_close(RECORD_LOADER); +} +void unlatch_tx_handler(bool persist) { + Cli* global_cli = furi_record_open(RECORD_CLI); + // Stash cliThread if not null + if(cliThread != NULL) { + CliCommand_internal* help_command = getInternalCliCommand(global_cli, "help"); + help_command->context = cliThread; + } + // Switch to new session + if(persist) { + // Use dummy debug firmware function as is_connected + cli_vcp.is_connected = &furi_hal_version_do_i_belong_here; + } else { + // Send CTRL-C + char eot = 0x03; + furi_stream_buffer_send(rx_stream, &eot, 1, FuriWaitForever); + } + cli_session_open(global_cli, &cli_vcp); + furi_record_close(RECORD_CLI); + // Unblock waiting rx handler + furi_stream_buffer_send(rx_stream, "_", 1, FuriWaitForever); + // Reconfigure stdout_callback to cli_vcp + if(cliThread != NULL) { + ((FuriThread_internal*)cliThread)->output.write_callback = cli_vcp.tx_stdout; + } + // At this point, all cli_vcp functions should be back. + furi_stream_buffer_free(rx_stream); + furi_stream_buffer_free(tx_stream); +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cli_control.h b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cli_control.h new file mode 100644 index 000000000..9ea959155 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cli_control.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +extern void latch_tx_handler(); +extern void unlatch_tx_handler(bool persist); +extern FuriStreamBuffer* tx_stream; +extern FuriStreamBuffer* rx_stream; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cligui.png b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cligui.png new file mode 100644 index 000000000..57a97049e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cligui.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cligui_main.c b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cligui_main.c new file mode 100644 index 000000000..4e7987c89 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cligui_main.c @@ -0,0 +1,133 @@ +#include "cligui_main_i.h" +#include "cli_control.h" +#include "text_input.h" +#include "console_output.h" + +static bool cligui_custom_event_cb(void* context, uint32_t event) { + UNUSED(event); + CliguiApp* app = context; + UNUSED(app); + return true; +} +static bool cligui_back_event_cb(void* context) { + CliguiApp* app = context; + UNUSED(app); + return true; +} +static void cligui_tick_event_cb(void* context) { + CliguiApp* app = context; + size_t available = furi_stream_buffer_bytes_available(app->data->streams.app_rx); + for(size_t i = 0; i < available; i++) { + char c = 0; + size_t len = furi_stream_buffer_receive(app->data->streams.app_rx, &c, 1, 100); + if(len > 0) { + furi_string_push_back(app->text_box_store, c); + } + } + if(available > 0) { + text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); + } + // Set input header stuff + size_t len = furi_string_size(app->text_box_store); + size_t idx = len - 2; + while(idx > 0) { + if(furi_string_get_char(app->text_box_store, idx) == '\n') { + idx++; + break; + } + idx--; + } + text_input_set_header_text(app->text_input, furi_string_get_cstr(app->text_box_store) + idx); + UNUSED(app); +} + +ViewPortInputCallback prev_input_callback; +volatile bool persistent_exit = false; +static void input_callback_wrapper(InputEvent* event, void* context) { + CliguiApp* app = context; + if(event->type == InputTypeLong && event->key == InputKeyBack) { + persistent_exit = false; + view_dispatcher_stop(app->view_dispatcher); + } + if(event->type == InputTypeLong && event->key == InputKeyOk) { + if(app->data->state == ViewConsoleOutput) { + persistent_exit = true; + view_dispatcher_stop(app->view_dispatcher); + } + } + if(app->data->state == ViewTextInput) { + text_input_input_handler(app, event); + } else { + console_output_input_handler(app, event); + } + prev_input_callback(event, app->view_dispatcher); +} + +int32_t cligui_main(void* p) { + UNUSED(p); + CliguiApp* cligui = malloc(sizeof(CliguiApp)); + cligui->data = malloc(sizeof(CliguiData)); + + latch_tx_handler(); + cligui->data->streams.app_tx = rx_stream; + cligui->data->streams.app_rx = tx_stream; + + cligui->gui = furi_record_open(RECORD_GUI); + cligui->view_dispatcher = view_dispatcher_alloc(); + cligui->view_dispatcher_i = (ViewDispatcher_internal*)(cligui->view_dispatcher); + prev_input_callback = + ((ViewPort_internal*)cligui->view_dispatcher_i->view_port)->input_callback; + view_port_input_callback_set( + cligui->view_dispatcher_i->view_port, input_callback_wrapper, cligui); + view_dispatcher_enable_queue(cligui->view_dispatcher); + view_dispatcher_set_event_callback_context(cligui->view_dispatcher, cligui); + view_dispatcher_set_custom_event_callback(cligui->view_dispatcher, cligui_custom_event_cb); + view_dispatcher_set_navigation_event_callback(cligui->view_dispatcher, cligui_back_event_cb); + view_dispatcher_set_tick_event_callback(cligui->view_dispatcher, cligui_tick_event_cb, 100); + + view_dispatcher_attach_to_gui( + cligui->view_dispatcher, cligui->gui, ViewDispatcherTypeFullscreen); + + view_dispatcher_send_to_front(cligui->view_dispatcher); + + cligui->text_box = text_box_alloc(); + view_dispatcher_add_view( + cligui->view_dispatcher, ViewConsoleOutput, text_box_get_view(cligui->text_box)); + cligui->text_box_store = furi_string_alloc(); + furi_string_reserve(cligui->text_box_store, TEXT_BOX_STORE_SIZE); + furi_string_set_char(cligui->text_box_store, 0, 0); + text_box_set_text(cligui->text_box, furi_string_get_cstr(cligui->text_box_store)); + text_box_set_focus(cligui->text_box, TextBoxFocusEnd); + + cligui->text_input = text_input_alloc(); + text_input_set_result_callback( + cligui->text_input, + text_input_result_callback, + cligui, + cligui->text_input_store, + TEXT_INPUT_STORE_SIZE, + true); + view_dispatcher_add_view( + cligui->view_dispatcher, ViewTextInput, text_input_get_view(cligui->text_input)); + + view_dispatcher_switch_to_view(cligui->view_dispatcher, ViewTextInput); + cligui->data->state = ViewTextInput; + + view_dispatcher_run(cligui->view_dispatcher); + + view_dispatcher_remove_view(cligui->view_dispatcher, ViewConsoleOutput); + view_dispatcher_remove_view(cligui->view_dispatcher, ViewTextInput); + text_box_free(cligui->text_box); + furi_string_free(cligui->text_box_store); + text_input_free(cligui->text_input); + view_dispatcher_free(cligui->view_dispatcher); + + unlatch_tx_handler(persistent_exit); + + furi_record_close(RECORD_GUI); + + free(cligui->data); + free(cligui); + + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cligui_main_i.h b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cligui_main_i.h new file mode 100644 index 000000000..b3b5823c9 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/cligui_main_i.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal_defs.h" + +#define TEXT_BOX_STORE_SIZE (4096) +#define TEXT_INPUT_STORE_SIZE (512) + +typedef enum { + ViewTextInput, + ViewConsoleOutput, +} CliguiState; + +typedef struct { + CliguiState state; + struct { + FuriStreamBuffer* app_tx; + FuriStreamBuffer* app_rx; + } streams; +} CliguiData; + +typedef struct { + CliguiData* data; + Gui* gui; + TextBox* text_box; + FuriString* text_box_store; + char text_input_store[TEXT_INPUT_STORE_SIZE + 1]; + TextInput* text_input; + ViewDispatcher* view_dispatcher; + ViewDispatcher_internal* view_dispatcher_i; +} CliguiApp; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/console_output.c b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/console_output.c new file mode 100644 index 000000000..933ee8431 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/console_output.c @@ -0,0 +1,13 @@ +#include "console_output.h" + +void console_output_input_handler(CliguiApp* app, InputEvent* event) { + if(event->type == InputTypeShort && (event->key == InputKeyOk || event->key == InputKeyLeft)) { + view_dispatcher_switch_to_view(app->view_dispatcher, ViewTextInput); + app->data->state = ViewTextInput; + } + if(event->type == InputTypeShort && event->key == InputKeyBack) { + char eot = 0x03; + furi_stream_buffer_send(app->data->streams.app_tx, &eot, 1, FuriWaitForever); + } + +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/console_output.h b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/console_output.h new file mode 100644 index 000000000..63e0d9afe --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/console_output.h @@ -0,0 +1,4 @@ +#pragma once +#include "cligui_main_i.h" + +extern void console_output_input_handler(CliguiApp*, InputEvent*); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/internal_defs.h b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/internal_defs.h new file mode 100644 index 000000000..cdb5f2fa9 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/internal_defs.h @@ -0,0 +1,118 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +typedef struct { + FuriThreadStdoutWriteCallback write_callback; + FuriString* buffer; +} FuriThreadStdout_internal; + +typedef struct { + bool is_service; + FuriThreadState state; + int32_t ret; + + FuriThreadCallback callback; + void* context; + + FuriThreadStateCallback state_callback; + void* state_context; + + char* name; + configSTACK_DEPTH_TYPE stack_size; + FuriThreadPriority priority; + + TaskHandle_t task_handle; + bool heap_trace_enabled; + size_t heap_size; + + FuriThreadStdout_internal output; +} FuriThread_internal; + +DICT_DEF2(ViewDict, uint32_t, M_DEFAULT_OPLIST, View*, M_PTR_OPLIST) +typedef struct { + FuriMessageQueue* queue; + Gui* gui; + ViewPort* view_port; + ViewDict_t views; + + View* current_view; + + View* ongoing_input_view; + uint8_t ongoing_input; + + ViewDispatcherCustomEventCallback custom_event_callback; + ViewDispatcherNavigationEventCallback navigation_event_callback; + ViewDispatcherTickEventCallback tick_event_callback; + uint32_t tick_period; + void* event_context; +} ViewDispatcher_internal; + +typedef struct { + Gui* gui; + bool is_enabled; + ViewPortOrientation orientation; + + uint8_t width; + uint8_t height; + + ViewPortDrawCallback draw_callback; + void* draw_callback_context; + + ViewPortInputCallback input_callback; + void* input_callback_context; +} ViewPort_internal; + +typedef struct { + FuriThreadId loader_thread; + + const void* application; + FuriThread* application_thread; + char* application_arguments; + + void* cli; + void* gui; + + void* view_dispatcher; + void* primary_menu; + void* plugins_menu; + void* debug_menu; + void* settings_menu; + + volatile uint8_t lock_count; + + void* pubsub; +} Loader_internal; + + +typedef struct { + CliCallback callback; + void* context; + uint32_t flags; +} CliCommand_internal; + +#define CLI_COMMANDS_TREE_RANK 4 +BPTREE_DEF2( + CliCommandTree_internal, + CLI_COMMANDS_TREE_RANK, + FuriString*, + FURI_STRING_OPLIST, + CliCommand_internal, + M_POD_OPLIST) + +#define M_OPL_CliCommandTree_internal_t() BPTREE_OPLIST(CliCommandTree_internal, M_POD_OPLIST) + +typedef struct { + CliCommandTree_internal_t commands; + void* mutex; + void* idle_sem; + void* last_line; + void* line; + void* session; + + size_t cursor_position; +} Cli_internal; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/text_input.c b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/text_input.c new file mode 100644 index 000000000..c5cc2ad39 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/text_input.c @@ -0,0 +1,33 @@ +#include "text_input.h" +#include "cligui_main_i.h" + +void text_input_result_callback(void* ctx) { + CliguiApp* app = ctx; + char* data = app->text_input_store; + size_t len = strlen(data); + for(size_t i = 0; i < len; i++) { + if(data[i] >= 0x41 && data[i] <= 0x5A) { + // Char is uppercase + data[i] += 0x20; + } + } + furi_stream_buffer_send(app->data->streams.app_tx, data, len, FuriWaitForever); + furi_stream_buffer_send(app->data->streams.app_tx, "\r\n", 2, FuriWaitForever); + data[0] = 0; + view_dispatcher_switch_to_view(app->view_dispatcher, ViewConsoleOutput); + app->data->state = ViewConsoleOutput; +} + +void text_input_input_handler(CliguiApp* app, InputEvent* event) { + if(event->type == InputTypeShort && event->key == InputKeyBack) { + // view_dispatcher_switch_to_view(app->view_dispatcher, ViewConsoleOutput); + // app->data->state = ViewConsoleOutput; + size_t len = strlen(app->text_input_store); + app->text_input_store[len] = ' '; + app->text_input_store[len + 1] = 0; + } + if(event->type == InputTypeLong && (event->key == InputKeyLeft || event->key == InputKeyRight)) { + view_dispatcher_switch_to_view(app->view_dispatcher, ViewConsoleOutput); + app->data->state = ViewConsoleOutput; + } +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/text_input.h b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/text_input.h new file mode 100644 index 000000000..77cc30efa --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/cli-bridge/text_input.h @@ -0,0 +1,5 @@ +#pragma once +#include "cligui_main_i.h" + +extern void text_input_result_callback(void* ctx); +extern void text_input_input_handler(CliguiApp*, InputEvent*); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/counter/README.md b/Applications/Official/DEV_FW/source/xMasterX/counter/README.md new file mode 100644 index 000000000..803c68634 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/counter/README.md @@ -0,0 +1,8 @@ +# Dolphin counter +This is a simple plugin for the [Flipper Zero](https://www.flipperzero.one). +It gives you access to a counter which you can increment and decrement using the up and down buttons respectively. + +![preview](https://github.com/Krulknul/dolphin-counter/blob/main/media/preview.gif) + +# How to install this? +I'd recommend using [flipperzero-ufbt](https://github.com/flipperdevices/flipperzero-ufbt), which is a lightweight tool for quickly testing Flipper Zero applications. The app will stay present on your device so it is not necessary to flash the entire firmware. diff --git a/Applications/Official/DEV_FW/source/xMasterX/counter/application.fam b/Applications/Official/DEV_FW/source/xMasterX/counter/application.fam new file mode 100644 index 000000000..f7b7f1e9a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/counter/application.fam @@ -0,0 +1,12 @@ +App( + appid="counter", + name="Counter", + apptype=FlipperAppType.PLUGIN, + entry_point="counterapp", + requires=[ + "gui", + ], + fap_category="Misc_Extra", + fap_icon="icons/counter_icon.png", + fap_icon_assets="icons", +) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/counter/counter.c b/Applications/Official/DEV_FW/source/xMasterX/counter/counter.c new file mode 100644 index 000000000..22fa8cd80 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/counter/counter.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include + +#define MAX_COUNT 99 +#define BOXTIME 2 +#define BOXWIDTH 30 +#define MIDDLE_X 64 - BOXWIDTH / 2 +#define MIDDLE_Y 32 - BOXWIDTH / 2 +#define OFFSET_Y 9 + +typedef struct { + FuriMessageQueue* input_queue; + ViewPort* view_port; + Gui* gui; + FuriMutex** mutex; + + int count; + bool pressed; + int boxtimer; +} Counter; + +void state_free(Counter* c) { + gui_remove_view_port(c->gui, c->view_port); + furi_record_close(RECORD_GUI); + view_port_free(c->view_port); + furi_message_queue_free(c->input_queue); + furi_mutex_free(c->mutex); + free(c); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + Counter* c = ctx; + if(input_event->type == InputTypeShort) { + furi_message_queue_put(c->input_queue, input_event, 0); + } +} + +static void render_callback(Canvas* canvas, void* ctx) { + Counter* c = ctx; + furi_check(furi_mutex_acquire(c->mutex, FuriWaitForever) == FuriStatusOk); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignCenter, "Counter :)"); + canvas_set_font(canvas, FontBigNumbers); + + char scount[5]; + if(c->pressed == true || c->boxtimer > 0) { + canvas_draw_rframe(canvas, MIDDLE_X, MIDDLE_Y + OFFSET_Y, BOXWIDTH, BOXWIDTH, 5); + canvas_draw_rframe( + canvas, MIDDLE_X - 1, MIDDLE_Y + OFFSET_Y - 1, BOXWIDTH + 2, BOXWIDTH + 2, 5); + canvas_draw_rframe( + canvas, MIDDLE_X - 2, MIDDLE_Y + OFFSET_Y - 2, BOXWIDTH + 4, BOXWIDTH + 4, 5); + c->pressed = false; + c->boxtimer--; + } else { + canvas_draw_rframe(canvas, MIDDLE_X, MIDDLE_Y + OFFSET_Y, BOXWIDTH, BOXWIDTH, 5); + } + snprintf(scount, sizeof(scount), "%d", c->count); + canvas_draw_str_aligned(canvas, 64, 32 + OFFSET_Y, AlignCenter, AlignCenter, scount); + furi_mutex_release(c->mutex); +} + +Counter* state_init() { + Counter* c = malloc(sizeof(Counter)); + c->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + c->view_port = view_port_alloc(); + c->gui = furi_record_open(RECORD_GUI); + c->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + c->count = 0; + c->boxtimer = 0; + view_port_input_callback_set(c->view_port, input_callback, c); + view_port_draw_callback_set(c->view_port, render_callback, c); + gui_add_view_port(c->gui, c->view_port, GuiLayerFullscreen); + return c; +} + +int32_t counterapp(void) { + Counter* c = state_init(); + + while(1) { + InputEvent input; + while(furi_message_queue_get(c->input_queue, &input, FuriWaitForever) == FuriStatusOk) { + furi_check(furi_mutex_acquire(c->mutex, FuriWaitForever) == FuriStatusOk); + + if(input.key == InputKeyBack) { + furi_mutex_release(c->mutex); + state_free(c); + return 0; + } else if((input.key == InputKeyUp || input.key == InputKeyOk) && c->count < MAX_COUNT) { + c->pressed = true; + c->boxtimer = BOXTIME; + c->count++; + } else if(input.key == InputKeyDown && c->count != 0) { + c->pressed = true; + c->boxtimer = BOXTIME; + c->count--; + } + furi_mutex_release(c->mutex); + view_port_update(c->view_port); + } + } + state_free(c); + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/counter/icons/counter_icon.png b/Applications/Official/DEV_FW/source/xMasterX/counter/icons/counter_icon.png new file mode 100644 index 000000000..4b8358b42 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/counter/icons/counter_icon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/README.md b/Applications/Official/DEV_FW/source/xMasterX/dice/README.md new file mode 100644 index 000000000..bfd9f73b8 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/dice/README.md @@ -0,0 +1,16 @@ +# Flipper Zero Dice App + +
+ +## Screenshots + +
+
+
+ +## Compiling + +1. Clone the [flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware) repository or another firmware that you use. +2. Create a symbolic link in `applications_user` named **dice**, pointing to this repository. +3. Compile by command `./fbt fap_dice_app` +4. Copy `build/f7-firmware-D/.extapps/dice_app.fap` to **apps/Tools** on the SD card or by [qFlipper](https://flipperzero.one/update) app. diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/application.fam b/Applications/Official/DEV_FW/source/xMasterX/dice/application.fam new file mode 100644 index 000000000..6ff28f302 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/dice/application.fam @@ -0,0 +1,13 @@ +App( + appid="dice_app", + name="Dice", + apptype=FlipperAppType.PLUGIN, + entry_point="dice_tool_app", + cdefines=["APP_DICE"], + requires=["gui"], + stack_size=1 * 1024, + order=90, + fap_icon="icon.png", + fap_category="Games_Extra", + fap_icon_assets="assets", +) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_1.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_1.png new file mode 100644 index 000000000..6f56f9644 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_2.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_2.png new file mode 100644 index 000000000..08c5872d9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_3.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_3.png new file mode 100644 index 000000000..c757caa66 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_4.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_4.png new file mode 100644 index 000000000..508184d14 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_5.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_5.png new file mode 100644 index 000000000..85831d239 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_5.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_6.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_6.png new file mode 100644 index 000000000..17cdbf105 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_6.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_7.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_7.png new file mode 100644 index 000000000..82f828a94 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/coin_7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d100_1.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d100_1.png new file mode 100644 index 000000000..a7f2c18b0 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d100_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d100_2.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d100_2.png new file mode 100644 index 000000000..783486976 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d100_2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d100_3.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d100_3.png new file mode 100644 index 000000000..92d5a5c0c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d100_3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d100_4.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d100_4.png new file mode 100644 index 000000000..324b7f633 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d100_4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d10_1.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d10_1.png new file mode 100644 index 000000000..d742026c9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d10_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d10_2.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d10_2.png new file mode 100644 index 000000000..0d9ca60ef Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d10_2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d10_3.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d10_3.png new file mode 100644 index 000000000..ab67316c6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d10_3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d10_4.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d10_4.png new file mode 100644 index 000000000..e89b03f3a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d10_4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d12_1.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d12_1.png new file mode 100644 index 000000000..053ead3cd Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d12_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d12_2.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d12_2.png new file mode 100644 index 000000000..752abaf33 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d12_2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d12_3.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d12_3.png new file mode 100644 index 000000000..711d18514 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d12_3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d12_4.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d12_4.png new file mode 100644 index 000000000..dff920c42 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d12_4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d20_1.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d20_1.png new file mode 100644 index 000000000..593adbad9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d20_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d20_2.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d20_2.png new file mode 100644 index 000000000..b8d11952d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d20_2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d20_3.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d20_3.png new file mode 100644 index 000000000..bea6e8ffc Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d20_3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d20_4.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d20_4.png new file mode 100644 index 000000000..1fa19dc4c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d20_4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d4_1.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d4_1.png new file mode 100644 index 000000000..8007cdca5 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d4_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d4_2.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d4_2.png new file mode 100644 index 000000000..8757c56e3 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d4_2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d4_3.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d4_3.png new file mode 100644 index 000000000..8d1687ac8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d4_3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d6_1.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d6_1.png new file mode 100644 index 000000000..f3b2ba293 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d6_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d6_2.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d6_2.png new file mode 100644 index 000000000..257c87a0d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d6_2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d6_3.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d6_3.png new file mode 100644 index 000000000..882be3a67 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d6_3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d6_4.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d6_4.png new file mode 100644 index 000000000..ac7928e2c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d6_4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d8_1.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d8_1.png new file mode 100644 index 000000000..b4c3b692e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d8_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d8_2.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d8_2.png new file mode 100644 index 000000000..705416e49 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d8_2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d8_3.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d8_3.png new file mode 100644 index 000000000..4c95fdcbf Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d8_3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d8_4.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d8_4.png new file mode 100644 index 000000000..a5f7fe838 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/d8_4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_back.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_back.png new file mode 100644 index 000000000..2c22d19c6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_back.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_down.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_down.png new file mode 100644 index 000000000..2954bb6a6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_down.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_exit.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_exit.png new file mode 100644 index 000000000..22f357913 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_exit.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_left.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_left.png new file mode 100644 index 000000000..0b4655d43 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_left.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_right.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_right.png new file mode 100644 index 000000000..8e1c74c1c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_right.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_roll.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_roll.png new file mode 100644 index 000000000..f20d7f565 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_roll.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_up.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_up.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_button_up.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_count.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_count.png new file mode 100644 index 000000000..a408de025 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_count.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_count_1.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_count_1.png new file mode 100644 index 000000000..ec61bde96 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_count_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_result_1.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_result_1.png new file mode 100644 index 000000000..6a512ad79 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_result_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_result_2.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_result_2.png new file mode 100644 index 000000000..393711c1d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_result_2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_result_3.png b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_result_3.png new file mode 100644 index 000000000..8a23ac36d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/assets/ui_result_3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/constants.h b/Applications/Official/DEV_FW/source/xMasterX/dice/constants.h new file mode 100644 index 000000000..f2fe03d31 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/dice/constants.h @@ -0,0 +1,116 @@ +#include +#include "dice_app_icons.h" + +#define TAG "DiceApp" + +#define DICE_TYPES 8 + +#define MAX_DICE_COUNT 10 +#define MAX_COIN_FRAMES 9 +#define MAX_DICE_FRAMES 4 + +#define DICE_X 45 +#define DICE_Y 6 +#define DICE_Y_T 0 + +#define DICE_GAP 44 + +#define SWIPE_DIST 11 + +const Icon* coin_heads_start[] = {&I_coin_1, &I_coin_2}; +const Icon* coin_heads_end[] = {&I_coin_7, &I_coin_1}; +const Icon* coin_tails_start[] = {&I_coin_5, &I_coin_6}; +const Icon* coin_tails_end[] = {&I_coin_4, &I_coin_5}; +const Icon* coin_frames[] = { + &I_coin_1, + &I_coin_2, + &I_coin_3, + &I_coin_4, + &I_coin_5, + &I_coin_6, + &I_coin_3, + &I_coin_7, + &I_coin_1, +}; + +const Icon* dice_frames[] = { + &I_d4_1, &I_d4_2, &I_d4_3, &I_d4_1, // d4 + &I_d6_1, &I_d6_2, &I_d6_3, &I_d6_4, // d6 + &I_d8_1, &I_d8_2, &I_d8_3, &I_d8_4, // d8 + &I_d10_1, &I_d10_2, &I_d10_3, &I_d10_4, // d10 + &I_d12_1, &I_d12_2, &I_d12_3, &I_d12_4, // d12 + &I_d20_1, &I_d20_2, &I_d20_3, &I_d20_4, // d20 + &I_d100_1, &I_d100_2, &I_d100_3, &I_d100_4, // d100 +}; + +typedef struct { + uint8_t type; + int x; + int y; + char* name; +} Dice; + +const uint8_t screen_pos[] = {}; + +static const Dice dice_types[] = { + {2, 0, 0, "Coin"}, + {4, 0, 0, "d4"}, + {6, 0, 0, "d6"}, + {8, 0, 0, "d8"}, + {10, 0, 0, "d10"}, + {12, 0, 0, "d12"}, + {20, 0, 0, "d20"}, + {100, 0, 0, "d100"}, +}; + +typedef enum { EventTypeTick, EventTypeKey } EventType; +typedef enum { SelectState, SwipeLeftState, SwipeRightState, AnimState, ResultState } AppState; + +typedef struct { + EventType type; + InputEvent input; +} AppEvent; + +typedef struct { + AppState app_state; + uint16_t roll_result; + uint8_t rolled_dices[MAX_DICE_COUNT]; + uint8_t anim_frame; + uint8_t dice_index; + uint8_t dice_count; + Dice dices[DICE_TYPES]; +} State; + +void init(State* const state) { + state->app_state = SelectState; + state->roll_result = 0; + state->dice_index = 0; + state->anim_frame = 0; + state->dice_count = 1; + + for(uint8_t i = 0; i < DICE_TYPES; i++) { + state->dices[i] = dice_types[i]; + state->dices[i].x = DICE_X + (i * DICE_GAP); + state->dices[i].y = i == 0 ? DICE_Y_T : DICE_Y; + } +} + +void coin_set_start(uint16_t type) { + if(type == 1) { + coin_frames[0] = coin_heads_start[0]; + coin_frames[1] = coin_heads_start[1]; + } else { + coin_frames[0] = coin_tails_start[0]; + coin_frames[1] = coin_tails_start[1]; + } +} + +void coin_set_end(uint16_t type) { + if(type == 1) { + coin_frames[MAX_COIN_FRAMES - 2] = coin_heads_end[0]; + coin_frames[MAX_COIN_FRAMES - 1] = coin_heads_end[1]; + } else { + coin_frames[MAX_COIN_FRAMES - 2] = coin_tails_end[0]; + coin_frames[MAX_COIN_FRAMES - 1] = coin_tails_end[1]; + } +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/dice_app.c b/Applications/Official/DEV_FW/source/xMasterX/dice/dice_app.c new file mode 100644 index 000000000..054e57999 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/dice/dice_app.c @@ -0,0 +1,267 @@ +#include +#include +#include +#include "constants.h" + +const Icon* draw_dice_frame; + +static void update(State* const state) { + if(state->app_state == SwipeLeftState) { + for(uint8_t i = 0; i < DICE_TYPES; i++) { + state->dices[i].x -= SWIPE_DIST; + state->dices[i].y = DICE_Y; + } + + if(state->dices[state->dice_index].x == DICE_X) { + state->app_state = SelectState; + state->dices[state->dice_index].y = DICE_Y_T; + } + + } else if(state->app_state == SwipeRightState) { + for(uint8_t i = 0; i < DICE_TYPES; i++) { + state->dices[i].x += SWIPE_DIST; + state->dices[i].y = DICE_Y; + } + + if(state->dices[state->dice_index].x == DICE_X) { + state->app_state = SelectState; + state->dices[state->dice_index].y = DICE_Y_T; + } + } else if(state->app_state == AnimState) { + state->anim_frame += 1; + + if(state->dice_index == 0) { + if(state->anim_frame == 3) coin_set_start(state->roll_result); // change coin anim + + if(state->anim_frame >= MAX_COIN_FRAMES) { + state->anim_frame = 0; + state->app_state = ResultState; + } + } else { + if(state->anim_frame >= MAX_DICE_FRAMES) { + state->anim_frame = 0; + state->app_state = ResultState; + } + } + } +} + +static void roll(State* const state) { + state->roll_result = 0; + + for(uint8_t i = 0; i < MAX_DICE_COUNT; i++) { + if(i < state->dice_count) { + state->rolled_dices[i] = (rand() % dice_types[state->dice_index].type) + 1; + state->roll_result += state->rolled_dices[i]; + } else { + state->rolled_dices[i] = 0; + } + } + + if(state->dice_index == 0) coin_set_end(state->roll_result); // change coin anim + + state->app_state = AnimState; +} + +static void draw_ui(const State* state, Canvas* canvas) { + canvas_set_font(canvas, FontSecondary); + + FuriString* count = furi_string_alloc(); + furi_string_printf(count, "%01d", state->dice_count); + + // dice name and arrows + if(state->app_state != SwipeLeftState && state->app_state != SwipeRightState) { + canvas_draw_str_aligned( + canvas, 63, 50, AlignCenter, AlignBottom, dice_types[state->dice_index].name); + + if(state->dice_index > 0) canvas_draw_icon(canvas, 45, 44, &I_ui_button_left); + if(state->dice_index < DICE_TYPES - 1) + canvas_draw_icon(canvas, 78, 44, &I_ui_button_right); + } + + // dice settings + if(state->dice_index == 0) + canvas_draw_icon(canvas, 48, 51, &I_ui_count_1); + else + canvas_draw_icon(canvas, 48, 51, &I_ui_count); + canvas_draw_str_aligned(canvas, 58, 61, AlignCenter, AlignBottom, furi_string_get_cstr(count)); + + // buttons + canvas_draw_icon(canvas, 92, 54, &I_ui_button_roll); + canvas_draw_icon(canvas, 0, 54, &I_ui_button_exit); + + furi_string_free(count); +} + +static void draw_dice(const State* state, Canvas* canvas) { + for(uint8_t i = 0; i < DICE_TYPES; i++) { + if(state->app_state == ResultState && state->dice_index == i && state->dice_index != 0) + continue; // draw results except coin + if(state->dices[i].x > 128 || state->dices[i].x < -35) continue; // outside the screen + + if(i == state->dice_index) { // draw dice with animation + if(i == 0) { // coin + draw_dice_frame = coin_frames[state->anim_frame]; + } else { // dices + draw_dice_frame = dice_frames[(i - 1) * MAX_DICE_FRAMES + state->anim_frame]; + } + } else { // draw first dice frame + if(i == 0) { // coin + draw_dice_frame = coin_frames[0]; + } else { // dices + draw_dice_frame = dice_frames[(i - 1) * MAX_DICE_FRAMES]; + } + } + + canvas_draw_icon(canvas, state->dices[i].x, state->dices[i].y, draw_dice_frame); + } +} + +static void draw_results(const State* state, Canvas* canvas) { + if(state->app_state != ResultState) return; + if(state->dice_index == 0) return; // skip for coin + + canvas_set_font(canvas, FontPrimary); + + FuriString* sum = furi_string_alloc(); + furi_string_printf(sum, "%01d", state->roll_result); + + // result text + canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignCenter, furi_string_get_cstr(sum)); + // ui frame + if(state->roll_result > 99) + canvas_draw_icon(canvas, 52, 26, &I_ui_result_3); + else if(state->roll_result > 9) + canvas_draw_icon(canvas, 56, 26, &I_ui_result_2); + else + canvas_draw_icon(canvas, 58, 26, &I_ui_result_1); + + furi_string_free(sum); +} + +static void draw_callback(Canvas* canvas, void* ctx) { + const State* state = acquire_mutex((ValueMutex*)ctx, 25); + if(state == NULL) { + return; + } + + canvas_clear(canvas); + + draw_ui(state, canvas); + draw_dice(state, canvas); + draw_results(state, canvas); + + release_mutex((ValueMutex*)ctx, state); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + AppEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + AppEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +int32_t dice_tool_app(void* p) { + UNUSED(p); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent)); + + FURI_LOG_E(TAG, ">>> Started...\r\n"); + State* state = malloc(sizeof(State)); + init(state); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, state, sizeof(State))) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + free(state); + return 255; + } + + // Set callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, draw_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() * 0.2); + + // Create GUI, register view port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + AppEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + State* state = (State*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // timer evetn + if(event.type == EventTypeTick) { + update(state); + } + // button events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + // lock input while animations + if(state->app_state == SelectState || state->app_state == ResultState) { + // input + if(event.input.key == InputKeyUp) { + if(state->dice_index != 0) { + state->dice_count += 1; + if(state->dice_count > MAX_DICE_COUNT) { + state->dice_count = MAX_DICE_COUNT; + } + } + } else if(event.input.key == InputKeyDown) { + state->dice_count -= 1; + if(state->dice_count < 1) { + state->dice_count = 1; + } + } else if(event.input.key == InputKeyRight) { + if(state->dice_index < DICE_TYPES - 1) { + state->dice_index += 1; + state->app_state = SwipeLeftState; + } + } else if(event.input.key == InputKeyLeft) { + if(state->dice_index > 0) { + state->dice_index -= 1; + state->app_state = SwipeRightState; + if(state->dice_index == 0) state->dice_count = 1; + } + } else if(event.input.key == InputKeyOk) { + roll(state); + } + } + // quit from app + if(event.input.key == InputKeyBack) { + processing = false; + } + } + } + } else { + FURI_LOG_D(TAG, "osMessageQueue: event timeout"); + } + + view_port_update(view_port); + release_mutex(&state_mutex, state); + } + + // Clear + free(state); + furi_timer_free(timer); + furi_message_queue_free(event_queue); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + delete_mutex(&state_mutex); + + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/icon.png b/Applications/Official/DEV_FW/source/xMasterX/dice/icon.png new file mode 100644 index 000000000..840088565 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/icon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/sources/coin.pixil b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/coin.pixil new file mode 100644 index 000000000..838aeafc0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/coin.pixil @@ -0,0 +1 @@ +{"application":"pixil","version":"2.6.1","website":"pixilart.com","author":"https://www.pixilart.com","contact":"support@pixilart.com","width":"35","height":"35","colors":{"default":["000000","ffffff","f44336","E91E63","9C27B0","673AB7","3F51B5","2196F3","03A9F4","00BCD4","009688","4CAF50","8BC34A","CDDC39","FFEB3B","FFC107","FF9800","FF5722","795548","9E9E9E","607D8B","ffebee","ffcdd2","ef9a9a","e57373","ef5350","e53935","d32f2f","c62828","b71c1c","ff8a80","ff5252","ff1744","d50000","fce4ec","f8bbd0","f48fb1","f06292","ec407a","e91e63","d81b60","c2185b","ad1457","880e4f","ff80ab","ff4081","f50057","c51162","f3e5f5","e1bee7","ce93d8","ba68c8","ab47bc","9c27b0","8e24aa","7b1fa2","6a1b9a","4a148c","ea80fc","e040fb","d500f9","aa00ff","ede7f6","d1c4e9","b39ddb","9575cd","7e57c2","673ab7","5e35b1","512da8","4527a0","311b92","b388ff","7c4dff","651fff","6200ea","e8eaf6","c5cae9","9fa8da","7986cb","5c6bc0","3f51b5","3949ab","303f9f","283593","1a237e","8c9eff","536dfe","3d5afe","304ffe","e3f2fd","bbdefb","90caf9","64b5f6","42a5f5","2196f3","1e88e5","1976d2","1565c0","0d47a1","82b1ff","448aff","2979ff","2962ff","e1f5fe","b3e5fc","81d4fa","4fc3f7","29b6f6","03a9f4","039be5","0288d1","0277bd","01579b","80d8ff","40c4ff","00b0ff","0091ea","e0f7fa","b2ebf2","80deea","4dd0e1","26c6da","00bcd4","00acc1","0097a7","00838f","006064","84ffff","18ffff","00e5ff","00b8d4","e0f2f1","b2dfdb","80cbc4","4db6ac","26a69a","009688","00897b","00796b","00695c","004d40","a7ffeb","64ffda","1de9b6","00bfa5","e8f5e9","c8e6c9","a5d6a7","81c784","66bb6a","4caf50","43a047","388e3c","2e7d32","1b5e20","b9f6ca","69f0ae","00e676","00c853","f1f8e9","dcedc8","c5e1a5","aed581","9ccc65","8bc34a","7cb342","689f38","558b2f","33691e","ccff90","b2ff59","76ff03","64dd17","f9fbe7","f0f4c3","e6ee9c","dce775","d4e157","cddc39","c0ca33","afb42b","9e9d24","827717","f4ff81","eeff41","c6ff00","aeea00","fffde7","fff9c4","fff59d","fff176","ffee58","ffeb3b","fdd835","fbc02d","f9a825","f57f17","ffff8d","ffff00","ffea00","ffd600","fff8e1","ffecb3","ffe082","ffd54f","ffca28","ffc107","ffb300","ffa000","ff8f00","ff6f00","ffe57f","ffd740","ffc400","ffab00","fff3e0","ffe0b2","ffcc80","ffb74d","ffa726","ff9800","fb8c00","f57c00","ef6c00","e65100","ffd180","ffab40","ff9100","ff6d00","fbe9e7","ffccbc","ffab91","ff8a65","ff7043","ff5722","f4511e","e64a19","d84315","bf360c","ff9e80","ff6e40","ff3d00","dd2c00","efebe9","d7ccc8","bcaaa4","a1887f","8d6e63","795548","6d4c41","5d4037","4e342e","3e2723","fafafa","f5f5f5","eeeeee","e0e0e0","bdbdbd","9e9e9e","757575","616161","424242","212121","eceff1","cfd8dc","b0bec5","90a4ae","78909c","607d8b","546e7a","455a64","37474f","263238"],"simple":["ffffff","d4d4d4","a1a1a1","787878","545454","303030","000000","edc5c5","e68383","ff0000","de2424","ad3636","823737","592b2b","f5d2ee","eb8dd7","f700b9","bf1f97","9c277f","732761","4f2445","e2bcf7","bf79e8","9d00ff","8330ba","6d3096","502c69","351b47","c5c3f0","736feb","0905f7","2e2eb0","2d2d80","252554","090936","c7e2ed","6ac3e6","00bbff","279ac4","347c96","2d5b6b","103947","bbf0d9","6febb3","00ff88","2eb878","349166","2b694c","0c3d25","c2edc0","76ed70","0dff00","36c72c","408c3b","315c2e","144511","d6edbb","b5eb73","8cff00","89c93a","6f8f44","4b632a","2a400c","f1f2bf","eef069","ffff00","baba30","91913f","5e5e2b","3b3b09","ffdeb8","f2ae61","ff8400","c48037","85623d","573e25","3d2309","fcbbae","ff8066","ff2b00","cc553d","9c5b4e","61372e","36130b"],"common":["000000","FFFFFF","7F7F7F","a1a1a1","C3C3C3","c40424","880015","B97A57","dba88c","ED1C24","f75b63","f26f9b","FF7F27","f7ab79","FFC90E","FFF200","cfc532","EFE4B0","1ee656","0c6624","22B14C","B5E61D","5487ff","00A2E8","99D9EA","3F48CC","7f86e3","7092BE","720899","cd55cf","A349A4","C8BFE7","ffffff"],"skin tones":["ffe0bd","ffdbac","ffcd94","eac086","e0ac69","f1c27d","ffad60","c68642","8d5524","896347","765339","613D24","4C2D17","391E0B","351606","2D1304","180A01","090300"]},"frames":[{"name":"","speed":100,"layers":[{"id":0,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAFlJREFUWEft1MEJACAMBEGv/6IvFeRxhoDC+hYMm0HZ9nnkiGGaTVCmI0oZyqTfF2Ywg5m0AGbSYvwzYzOS1iuuP5C4YZixmST37V3WxJpSO5jBDGbSAl+YKXbCj5ghLqGvAAAAAElFTkSuQmCC","edit":false,"name":"Layer 1","opacity":"1","active":true,"unqid":"zdzpbp","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":1,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAf5JREFUWEftVkFywjAMVAZooP//apukScYdq7ariJXsAAcO4UICtrxerVbq6I0+3RthoQOMlY1nmAkNKd4Vf9fidLgEkffH36znjDn+/0lE369gJoPYHBpCCF3XUQiBuvhA8fH/WYGMOEwCWpiRIO4Y2AmGwVqAamDyRvTdIJmyRIJ4CAxipOhlnucqmMvlwiBOpxOt65pTpNO9QYyChmEY6Ha76QD8Po5juF6v+baterRYdsGgkpXptKrJY0qKXq8rsZFm+LBYEcuyUKKaf0pR9B5LA1D4XGqRXi48/phgSoBcJeIaFvA9RcCHW7HRLbNnuJ5glChKYWYV+pNkx6K8UJ9u8SgrmdicHdcYkTBdYxItAbKq1IlaRAGY9FKk8UowUuQ6xUjkstT/NC1uYtm+vIlZll5dO/qSXtUExhKlpruC564nucxI1VsjgU5FreIkQJ0q7ch37XyTqth/zuczNKgaDeB/dMGNkdZKlgNM0xT6vvd6Uc34aJ7nkBunMYjBQScC+CGivrGM9UDlkSbZ+UqTn9ubpC48n/D0oAGVdKzryiOFdF74oiIsRBR36Y7L744zIyCWhWzW1nKNOnWt1LUvofEBnlsDA11VsIJmHwYjhnKUdqirFjCWA7fs1Sx+pOJ4GowWbKvVtIDmWM0LW09+Zt0BxmLvYMZi5hdntuIk2wS1xwAAAABJRU5ErkJggg==","edit":false,"name":"Layer 2 Copy","opacity":"1","active":true,"unqid":"","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":2,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAmlJREFUWEelWOt6qDAIs+//0O5rV1hIA9Rz9mdOsYQQLm48//bzXr42Lu2W2RfjAOB9azxjHEe3vlqD53ncKwMQDleEjV3qswOzgODhCoA9r56tNPyxJf1WYAIQdETgjjPebaDeqQBlYCSQTieZWA2AYDD4V2CugIxMMBuRsWMAbwBJMFUE00kHxAAIWxWoY2AwboyRoPMEjKrzwbb7b6841s8BBlmBa7cjB6razNYAhnetqoj9ZSPBcH73AYrFdQ/1IVK4Hs/7XGX7vuNAByFFXfnuZhjAFOmcZzsYZAdTFcB8SNE6HIVKTc2jBZ0wcG+mG1AYIEZnaOlJtAGMpUpVWQXGunsLxmg1B5TvtD9R47OKYqbm+1y5mhkUXAYm6SHmxH+DnevGxDwZQREfmgGD0CfUvGHdUFN2jeyADp1dgdlUc9PCxtZNe88WMwNpvEsTCMtzyxpCbVQjgsBgb8O0/RYjIk1KW4nPq/rrtlj5uAGD7PCWkI2JbJvwZm16gf6UM2OpglFw9BZuesnYWOPCRAzV0zc9c0DIefh5Y8SZA++mS5uBEatsZIbYCA6RBSjVLB1c1nbWZIjP9eme7jOQSw84afu8y1Q6yvbqeoXgbU98fvDED4HdrJ3VCrEChx7DAuNPFtTR/wDx1iEHXqF4zzeWJDdALALQ2rokzTiQcAEHhi9IrCqhIwUuiJqrBgPdhk5INWPCfoOVpiIndgIg/piz/YVLsQSDFPNnUvdBp+zVVEdAN9PXGRIUZ33G72OTyxgJvaM5UWqIZ0s2ACuNfEkT26bCroDBIW0WWgPB2u1/rb6wv2x/ACDUeEL6Qq7OAAAAAElFTkSuQmCC","edit":false,"name":"Background","opacity":"1","active":false,"unqid":"u6oba5","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":3,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAfZJREFUWEftVl1vgzAMNCoM2P//qxtQiDw5JZkxdj5YH6qpeULCdu7OziUNvNBqXggL/AswWKFoMeHiQACQAEpyeQ7FfwDA3SJypSAgIjZNQ7mmQogIewztHeKS++XAaEUkW4uozM3mpcBsAHADiEN+KM7U4cw5sFCb8vg+pkoWGI1VKlb7x2vEb+cc3m7E0a9DnlWEhqzfEyQzzt7/I5V85cccycXz+fcXAHxyQEXJy7Jg3/cyVgNpzQWu6wpd14WhV1uobRDk8wXati1hnLMdbW74afQ4LLap1oSBtYiUDDGvEfeypvwKGHmqZO0U8cfIMRpy+muY17ZJKlMMJmdWORUD0OzclChjmZbqFQmJqsCoQ7U7be3GZ7M5epG3Jna/maeJLkIKtE4bB5ZroSZUNErmkcnNvKsKQ9VaGj1JsQnNiX28Vdu8U0idbduCa/Jjm3NtUw1f5Lddp9anLjjNvFJeYinB2629fyIG8yaepgnGceRtiEzmecZhGLida2rk3P1kCRYY2ZaQGJnRvZVb4WKkJ4NzThI77Z0Cox31E6gcoP0/v61Nk8yBsRSKQMPJ4L6hecj+DpZPiAOXEjAhQX39VYLRvCsCqgEjQclToj2YpCnSy+7bau0VMBqop9T/C5jC2S0Pe4N5Sk/LBb8W+W6TpdsPAVbkJHCu0MYAAAAASUVORK5CYII=","edit":false,"name":"Layer 2","opacity":"1","active":false,"unqid":"","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":4,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAKFJREFUWEftVVsKwCAMa+9/6I4Oiw/cap2Cg+zHQWuaJXEyHfTwQVwIZJ7cgDJQJnpSkRlkBpmJKjCSGVkFOonD5dEWkcyHuaq5+Lo5sqcclkb1yViRiJSdEq7WNJiMvPa/vVu9Xe0Lt5BJytzES8KfyCjjiOSuh50G16YZ0IV7YiFdOLgLhYty5Ke32wUXHzbBJjckTQMyg8wgM1EFfpGZCziRWCSDveGOAAAAAElFTkSuQmCC","edit":false,"name":"Layer 4","opacity":"1","active":false,"unqid":"","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":5,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAfhJREFUWEftl91uwjAMhR21HbC9/6sOOkCZ0tWVe+q/VprEBVyh4iZfju3jUOiFPuWFWOgNY2XjiDI1kdoj64Zp+iSibyKSALxRe2Z9Z95dUFEwQ6w2rbXWUqZHhb/XWqn8PUTIKS6hphvEi24U2AnDgCGUR4wwmVqRAkiVtINtxLJgZHpq13X0fD6nk93v9xBqGAYPylRIg9EUwTgPqNxut3o+n2V6GKBer1e6XC4qUGYTWbyySxDIiuN3tAOs9ldhWmdM6HPLQIdkGgMBpn1aivu+b+uqrS9hFnJoU5RUdpd2atd7vLU3MIZfyE2t1KLHtHdU8HmPjToIs5gYG5pIl6XIZg3Io/fe6jcsOu10Vo1EIwLfW9nF7Morh49gcBxoYNaM8mKlfSw1mYGJ5koW5uWUwUZQ0yQrXys4z1v2KINtrxYwwyy2DWPfAsTnZlxr58fjQfPcUie55hkr+uaawzBYXRZafPOacRzpdDrhGhtwDabd7L7mvIQnb3cbGB2eFfB+IxF94KXLmtrTlaHrOlXOLKigwrpKTW11wDmbowrs4JplcF22i1Gvyed5iOWw6qlABW0qhwYaGRp2GYn7r1UbljrRIcJbeyuyH+OvSuQ98vfMoUMYa9h5IFqKMvG7YVKLHg1KyXd08b3vvWHMNtwr5X/G/wLO/uskz2jgOQAAAABJRU5ErkJggg==","edit":false,"name":"Layer 2 Copy Copy","opacity":"1","active":false,"unqid":"","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":6,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAkBJREFUWEetl1t2wyAMRO39L9o9pkgZDSMJ2vQrTQRcRk/u629/z+aye9NumJ0YB4DnqXnue9m6Pas1uK7LT2UAceC4YWOXntnBDBDcXAHY79Vvww0fteS5FUwAwYMIbtnjmQZqTQWUwUiQBKKNJQMQCobzFUwLMiW3ta/98tnUsWzaAZIwzQ3w8J3MZViPwQnoDAzjquBN7o+jx4Xh/xLmNZ62A2j+7xnH8bPAoCrw+bXDDXGdKjit4qT+sJcwiX+34ysrppxlU2XnWG5oBl36QjEc8kNQZwq+seIqvhcGmN/l4HS3NUPKmrEZxg8GDMRSCHBa40B0hoahwPXMnJUYYQKLSm+xJkuQGmZu7rckV3AWyVqTXMBsGWx1E/jSM8g2RbcJl6kiyK4dW2GaYxAvMVPBFPXlWBmrOS3M9EFQpim1xzCWjVswnHbfDGCMR6jKZ6mdqGO1Q8bMEukwXnAVPqozGzAMtizBKrwNgxNe0xjb3oSZx80SJkDtJjNQjTILZCr1WR/zzi1G2QhDamCr93j48ghhGbtUYCtIPPyElmBFq0n1qlWo/esRgqc98fz499hZjRBBGdFV+cmCMKiEjxQu6YwGHty6Sc/bUDFAo8zpMwWyxGMPv2NVxm/C9+EFiQrxAbb25BFHlT0wZO+mRSHMNJZfVNnwFT/m+FXAGaKSY1EopMjhw79SZAcGvDCeJzyzbmU3ztSZIkcwGBsIlTxrQh3ZUeQExlkUlAV1BgbyVfE5zFqDKtu2/HRwxg9BIkRCZQ/enQAAAABJRU5ErkJggg==","edit":false,"name":"Background Copy","opacity":"1","active":false,"unqid":"u6oba5","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":7,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAfpJREFUWEftV9tugzAMdQQM2P7/VwcUUKakJHKMHZuqk3hon6piHx8f36iDG33cjbjAh4xUjavK+AtlvYqtlukLAB4AgElYglDSFh+VTBAiAUdA7713rortDptkZE6khsqBFMSEkknBVV+JDOcYfsP2XP9IqmCVdgBouUQ4MjHIvu/QNE14rmZkaGqciIhHyQTDXwD4OQJwahQ+Qg9hJbjvCwCE4SiwODKF87quvuu6pJCoJAI+JbAsC/R9TzGoHdsDXOCTI1IufKW9dErSew/btkHXdcn+VC4ORApMW0PaJRb/ZFPY0l3ASUkz5/qVNqhWTkwm41vIWLanhUxaoDjholQWMrdRxrBCoolVmYRXLVMGRHsjbtMoy/MWWbc118DSvmIbOB/EEN89o6sLT9jOml/MER3bnCR7awghrIrlPnEq5jVQwT5JH52O6iSFaE9Y++j0+hG2edu2rCrVDFBEOnFUqWRas8PEcDLV2yTtAu6GSQq5eZ79MAz44ucTME0TjOPIDoRlQnzTNPGVIkxUkFqr03F/sGLsKFOc2nal90MlQcDppq1d/uh6hYy4i8KYCivBpAhtOkl5euYjOF2MChkt6RxbO4Lfx5sfLlHx8nUg0XNgTbYQQSPDKWbpnVdwTf+btOF52/OXMnhbdGb8/gv7Mu5HGUmyP5KE6CQNV8vwAAAAAElFTkSuQmCC","edit":false,"name":"Layer 2 Copy","opacity":"1","active":false,"unqid":"","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}}],"active":true,"selectedLayer":1,"unqid":"rs6rne","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAdFJREFUWEftV9uqg0AMXKko6v//quL9ECESY25r+1AO7UuL7mYnM5NkW+z7vqcv+RQ/MIoS/4OZoihcp+XaMZsZCgIPg2fab0QM7/u+T23bqkmEwSCICAALmMWWC4aCsBiIgqQxOEUmGNwofbuGIQsAKI8h7VfBSIxQv8zz7OIpy/IA8Xq90rquh6+43DSICAY2DMOQmqa5BICNEHAcx1TX9RlYQsW9obFsgpFKlgbWqsmiifqJr7vE5uOA0rgsSwKq4aPRqxlSMz6CkeJdZJLKl/aJm/tJf9GYofKgzDS5yzPKjJfNRV8BiCQhHhzpPTdmaBlaPrD6hceg2q+QmU+ywqWVgEvyncy8C0bzAZeJGpj3nTAYr+RzJX3EjGZKnqHXjrlUJhhKp2YwLgUty3fAnJVmlTbMH5wvOQdLwKQEb31N6sC8J0zTlKqqUmdR5EaH3dxkXQIDB8MwtCqBlm+059B1XdcdNz9zNklzyDss8h5l3rbtuFJIsqv3GfAK3kGsQfnEuJr/3Jse3+iVOi15bzDexob3j9I6XGqEfBTksOpeyHmm0mGaVDwRrEp1vceM1jM8r+SAPhN+AiYKJHddWKbcwE/W/8B81MBPJIjs+QPyMEG2lHD1VgAAAABJRU5ErkJggg==","width":"35","height":"35"}],"currentFrame":0,"name":"Untitled","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMA/sfR5H8Fkddasdmnacvx//8745jkhasdASD945kjknhj/AAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAdFJREFUWEftV9uqg0AMXKko6v//quL9ECESY25r+1AO7UuL7mYnM5NkW+z7vqcv+RQ/MIoS/4OZoihcp+XaMZsZCgIPg2fab0QM7/u+T23bqkmEwSCICAALmMWWC4aCsBiIgqQxOEUmGNwofbuGIQsAKI8h7VfBSIxQv8zz7OIpy/IA8Xq90rquh6+43DSICAY2DMOQmqa5BICNEHAcx1TX9RlYQsW9obFsgpFKlgbWqsmiifqJr7vE5uOA0rgsSwKq4aPRqxlSMz6CkeJdZJLKl/aJm/tJf9GYofKgzDS5yzPKjJfNRV8BiCQhHhzpPTdmaBlaPrD6hceg2q+QmU+ywqWVgEvyncy8C0bzAZeJGpj3nTAYr+RzJX3EjGZKnqHXjrlUJhhKp2YwLgUty3fAnJVmlTbMH5wvOQdLwKQEb31N6sC8J0zTlKqqUmdR5EaH3dxkXQIDB8MwtCqBlm+059B1XdcdNz9zNklzyDss8h5l3rbtuFJIsqv3GfAK3kGsQfnEuJr/3Jse3+iVOi15bzDexob3j9I6XGqEfBTksOpeyHmm0mGaVDwRrEp1vceM1jM8r+SAPhN+AiYKJHddWKbcwE/W/8B81MBPJIjs+QPyMEG2lHD1VgAAAABJRU5ErkJggg==","palette_id":false} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d10.pixil b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d10.pixil new file mode 100644 index 000000000..3356c453c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d10.pixil @@ -0,0 +1 @@ +{"application":"pixil","version":"2.6.1","website":"pixilart.com","author":"https://www.pixilart.com","contact":"support@pixilart.com","width":"35","height":"35","colors":{"default":["000000","ffffff","f44336","E91E63","9C27B0","673AB7","3F51B5","2196F3","03A9F4","00BCD4","009688","4CAF50","8BC34A","CDDC39","FFEB3B","FFC107","FF9800","FF5722","795548","9E9E9E","607D8B","ffebee","ffcdd2","ef9a9a","e57373","ef5350","e53935","d32f2f","c62828","b71c1c","ff8a80","ff5252","ff1744","d50000","fce4ec","f8bbd0","f48fb1","f06292","ec407a","e91e63","d81b60","c2185b","ad1457","880e4f","ff80ab","ff4081","f50057","c51162","f3e5f5","e1bee7","ce93d8","ba68c8","ab47bc","9c27b0","8e24aa","7b1fa2","6a1b9a","4a148c","ea80fc","e040fb","d500f9","aa00ff","ede7f6","d1c4e9","b39ddb","9575cd","7e57c2","673ab7","5e35b1","512da8","4527a0","311b92","b388ff","7c4dff","651fff","6200ea","e8eaf6","c5cae9","9fa8da","7986cb","5c6bc0","3f51b5","3949ab","303f9f","283593","1a237e","8c9eff","536dfe","3d5afe","304ffe","e3f2fd","bbdefb","90caf9","64b5f6","42a5f5","2196f3","1e88e5","1976d2","1565c0","0d47a1","82b1ff","448aff","2979ff","2962ff","e1f5fe","b3e5fc","81d4fa","4fc3f7","29b6f6","03a9f4","039be5","0288d1","0277bd","01579b","80d8ff","40c4ff","00b0ff","0091ea","e0f7fa","b2ebf2","80deea","4dd0e1","26c6da","00bcd4","00acc1","0097a7","00838f","006064","84ffff","18ffff","00e5ff","00b8d4","e0f2f1","b2dfdb","80cbc4","4db6ac","26a69a","009688","00897b","00796b","00695c","004d40","a7ffeb","64ffda","1de9b6","00bfa5","e8f5e9","c8e6c9","a5d6a7","81c784","66bb6a","4caf50","43a047","388e3c","2e7d32","1b5e20","b9f6ca","69f0ae","00e676","00c853","f1f8e9","dcedc8","c5e1a5","aed581","9ccc65","8bc34a","7cb342","689f38","558b2f","33691e","ccff90","b2ff59","76ff03","64dd17","f9fbe7","f0f4c3","e6ee9c","dce775","d4e157","cddc39","c0ca33","afb42b","9e9d24","827717","f4ff81","eeff41","c6ff00","aeea00","fffde7","fff9c4","fff59d","fff176","ffee58","ffeb3b","fdd835","fbc02d","f9a825","f57f17","ffff8d","ffff00","ffea00","ffd600","fff8e1","ffecb3","ffe082","ffd54f","ffca28","ffc107","ffb300","ffa000","ff8f00","ff6f00","ffe57f","ffd740","ffc400","ffab00","fff3e0","ffe0b2","ffcc80","ffb74d","ffa726","ff9800","fb8c00","f57c00","ef6c00","e65100","ffd180","ffab40","ff9100","ff6d00","fbe9e7","ffccbc","ffab91","ff8a65","ff7043","ff5722","f4511e","e64a19","d84315","bf360c","ff9e80","ff6e40","ff3d00","dd2c00","efebe9","d7ccc8","bcaaa4","a1887f","8d6e63","795548","6d4c41","5d4037","4e342e","3e2723","fafafa","f5f5f5","eeeeee","e0e0e0","bdbdbd","9e9e9e","757575","616161","424242","212121","eceff1","cfd8dc","b0bec5","90a4ae","78909c","607d8b","546e7a","455a64","37474f","263238"],"simple":["ffffff","d4d4d4","a1a1a1","787878","545454","303030","000000","edc5c5","e68383","ff0000","de2424","ad3636","823737","592b2b","f5d2ee","eb8dd7","f700b9","bf1f97","9c277f","732761","4f2445","e2bcf7","bf79e8","9d00ff","8330ba","6d3096","502c69","351b47","c5c3f0","736feb","0905f7","2e2eb0","2d2d80","252554","090936","c7e2ed","6ac3e6","00bbff","279ac4","347c96","2d5b6b","103947","bbf0d9","6febb3","00ff88","2eb878","349166","2b694c","0c3d25","c2edc0","76ed70","0dff00","36c72c","408c3b","315c2e","144511","d6edbb","b5eb73","8cff00","89c93a","6f8f44","4b632a","2a400c","f1f2bf","eef069","ffff00","baba30","91913f","5e5e2b","3b3b09","ffdeb8","f2ae61","ff8400","c48037","85623d","573e25","3d2309","fcbbae","ff8066","ff2b00","cc553d","9c5b4e","61372e","36130b"],"common":["000000","FFFFFF","7F7F7F","a1a1a1","C3C3C3","c40424","880015","B97A57","dba88c","ED1C24","f75b63","f26f9b","FF7F27","f7ab79","FFC90E","FFF200","cfc532","EFE4B0","1ee656","0c6624","22B14C","B5E61D","5487ff","00A2E8","99D9EA","3F48CC","7f86e3","7092BE","720899","cd55cf","A349A4","C8BFE7","ffffff"],"skin tones":["ffe0bd","ffdbac","ffcd94","eac086","e0ac69","f1c27d","ffad60","c68642","8d5524","896347","765339","613D24","4C2D17","391E0B","351606","2D1304","180A01","090300"]},"frames":[{"name":"","speed":100,"layers":[{"id":0,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAEpJREFUWEft1MENACAIADHZf2kn8HE/TOoE5KjMWfRm0SzHMK9tKKNM/anMMMNMLcBMLebOMMNMLcBMLebOMMNMLcBMLebOfGHmAm5UACTjh/FnAAAAAElFTkSuQmCC","edit":false,"name":"Background","opacity":"1","active":true,"unqid":"vbd6q","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":1,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAhVJREFUWEftV9GOwjAMW7f//+R2p+Sa4npJ2wEPCIF04hgjcRzHzdJ5nuf2Ia/0AxN04ruZSSlNFRjJNGRmJWiU1UuG8W6BWfmhAZF7Jbi9y/X0H0Bng4pq171CXGY8Viiw9UJsQRPQuyKxOMbErMgLGPtBznk7jqMrYJV+LylcC9lxwQgQezEgRDcCZ9+VUqQoZTLnfO773rUU44VgBISBGgHi3iNA0w7co/lQX1Mw9Qalk9sVTkIdadCHaUk1bbq6DQYB4P+SyP4kAzDWKpbrVezeRD3HjAUspWifvYnw9EMj3m7B8V9uU/WJ5h0iQANDE+HI5eErkhzF+1SbDIy1xwGDHtO8r7ZH1xH2FwKFt3YFhabH4w2m18A4Rtg2EgRUwWhi/A0PwxCMeQIFYWbQgbuqPQdG7S2D8aqowdn+sVUXZiKT9EQ8PLWZUgCjWKt/UL5exA0prBZ8sLZ7orUTT2NkSbQk7atC7VoWgG9aQXFbzKEDYyVecAODVs8epJ9L2VI9k0xI7EFLmuEKIgGHq8EDCD55XKZwGYxH6Wg18E5p2nM0JC9d3cE6elTh5NGy5DDUnVW8gEXjPXw6GG1mEbDq3O6J/1ZmeEw9Rmjk2/hjB6IibzGzopluVFPq4ke79dRnPLPyvIGSdwef9yFazlTds2ftSMRR1lGyGdJbYCzYKwlHgKZgZtW88/sfmIjNj2LmD0cIcLZmv4arAAAAAElFTkSuQmCC","edit":false,"name":"Layer 1","opacity":"1","active":false,"unqid":"syuab","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":2,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAjJJREFUWEfVl9u2gyAMRKX+/yerXaEMaxgSQHseevpSeyHsJDNB0/ZDr/RDLNv/gbmu64oql1L680SGAQ3G23PAmNmfgt6CMYhZQWagI9gQBi3SzUebzUANxFuPSg5hOLhWBUHpPznWRGedBEvcvPYWDCIBwAHizZrYDOkk+Rym9D1vrEDHcWz7vgPE3GjXcGU6juN6vV5Ve48rE1VEgCoItRZATSst3hTGE68G1org83meTWVEaxmKv1uC8VxUvmtaoLMoQ53nlj6t4qHZVYwSijWDDZwMNtNE6XktvVZSgKCtPEC5olyV0E0rMDS8cvZe29Q1LHys4WndWZuz9CpD46C6tQTkz43TAohGvG5lWAMDGLUsYnVAPHh4+qqThjCkjZqlI+AKEVSsm0W3NIPKGAxe+76jpJxAN9DK/zvrFrtrUuttKpN0s0DmHpkzWX/UAm/QsairNtkceoK75wf1cwSjh15UkeY4EKc2+3cwPPLtmisDV8j5U1z6KZTMkgqCysr7HIbFi3ZhLlgwa5vnEk2EDskIag3GRGvB7JQt193sYKDgEE1yXCjUOgwcxTAqFEc4tV3lN96w6kqPgm7ORLaOAIJbyG628E3cIwGzVgAz2hyagoh1DYwguhq3SYNp0OCmu7ldiO51HF3FMPCod9qOtMJ25mTkxqqG8PTSaYYHxspjxxMtoZ3eg577dDB63Ji5aSWJ5crMNhv9fieJ5cp8A/TN2vAh7pugT9f+FMwbzj9NQl0XLG4AAAAASUVORK5CYII=","edit":false,"name":"Layer 2","opacity":"1","active":false,"unqid":"n3umw","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":3,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAj5JREFUWEfNVwGOwyAMK93/n8yYwhHOMQnQ3k7qpGnayoLjOCak40Gv9CAsx/PBlFLKLmMppa8l5AYSMDt7XMA85OYlsQXG23QHbMSuxmNAKzDyvJdMgjQQSUsp3/X3FVOYQPuP2X8AwyWCLGqiFESAKmD8rEth/UCSx84MTGeFAekm8snZ8q5czpzz8Xq9vMTG1lZmBMD7/ZY/VsA553KeZw2iG1zVkgDRlwDiUoXMwEJs864VZmVlBQpEQCg7d8GgkI14sWSzEkF5TPmxo7aZ4U7CUrWAlUFlQJ7rW39XrdxlpretZo1i9oSN7OBz0d95npVVJ5EfLTK1KGDI2IiYgrlxuLUbmMGTwjKhkSHQWSdBMCP0lmQNKeAVTFSiISOHwhpTAkhbc0urGzsGp4x3MKu2DsHMdEE66ZuCE2PcDka7rnnX4DFLMJGXOIC4RP2YgC40zbA8m0i8XdvotBviVSCNjH64TsU71QyamXPa4tmioGvJtAmoHGa9x8qyTDAedAsoOZcjFnONKYD+BMabT6C97flEgBwWh25iM5xOekFbmzOptfDyFI/KSqAHw/2lv9l00Cne4daHJ+y6YBgzzRAN8QMYRc+GRoddElfGIckD1JUNF4hIvEbA0bjZNmFzq5jR6DxGvgZGA5HHmOGKN4vG09UgbrwB/QFVv3Mdmd0I+P/eEG72wy/Ufu6tUsRHp3sUwp2FZjfQ21fT1RXYY3Um3sGBwxQvPpgB/RdmLuLbWn67TFvRLy56FJgPoS1QQmVa3RoAAAAASUVORK5CYII=","edit":false,"name":"Layer 3","opacity":"1","active":false,"unqid":"syuab","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":4,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAiVJREFUWEfdl9uOwyAMRHP5/09OUxkxaBhsQ6OtVO0+dUuCj8djQ/fth/72H2LZ/j/Mfd93pPi+76EAj5XJAhqIF9MYH8HMgkUBWZEavH21DOMFTxTtAiQl6ZZQvUidViaDiaRdqH/xCAVTNZbUCWHYg04mg0EVRN5/DmMbvV6v7TxPg0VgfC4JXNd1n+dZgmQg6q3MN4MyTl0NpoCQIfFesxpK7IFx+T+CQaZ1g6aMgpgyx3EUZQIQTUBVHMZKpww29hQgE+/3dd3bcXSzhDKGkjWvMluGrvI6qnsKHcVSq6wKImUtILZP9VzxEytnn6NSuTBIY9aqznoBsff/DMabnACM/AEI8tpQJuwbDb+0TNkZQ2vc/uXrOg4GAzsl6+IveWbSMUUBggCQq4yUbw4j7W0DbrM2ViNWAMCoKh2M7YE/G5aeiQdlpL3d1ndmEQO5ypgilpBBLcEgFWm/dhxg0CUKdfOEpzZgMhMPU5BnDUG1ZuKukdZue+Hc4nWFIXXbe1OYSpF2iNd1FtySgYpcnsjEIYxAsOHCw1FrxMdAhSlJRb5ZUYafCS9RCqL/o5vMvE9hOpDZvSUakgCbtXdaJlyy9KoA82lwvt01x8uJDVWWDZxMXPeeK9O3Vcg7aLHonU+DMjxrnKExXKR4SM58M7vxLcGsyD8D4fWlU5skdH+2eEp9AsHPTm96DLMaJPu5urpHM/unL3zzedcz3wyY7f1TMG9hxkxC7RN4kQAAAABJRU5ErkJggg==","edit":false,"name":"Layer 4","opacity":"1","active":true,"unqid":"n3umw","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}}],"active":true,"selectedLayer":4,"unqid":"rbcl7d","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAiVJREFUWEfdl9uOwyAMRHP5/09OUxkxaBhsQ6OtVO0+dUuCj8djQ/fth/72H2LZ/j/Mfd93pPi+76EAj5XJAhqIF9MYH8HMgkUBWZEavH21DOMFTxTtAiQl6ZZQvUidViaDiaRdqH/xCAVTNZbUCWHYg04mg0EVRN5/DmMbvV6v7TxPg0VgfC4JXNd1n+dZgmQg6q3MN4MyTl0NpoCQIfFesxpK7IFx+T+CQaZ1g6aMgpgyx3EUZQIQTUBVHMZKpww29hQgE+/3dd3bcXSzhDKGkjWvMluGrvI6qnsKHcVSq6wKImUtILZP9VzxEytnn6NSuTBIY9aqznoBsff/DMabnACM/AEI8tpQJuwbDb+0TNkZQ2vc/uXrOg4GAzsl6+IveWbSMUUBggCQq4yUbw4j7W0DbrM2ViNWAMCoKh2M7YE/G5aeiQdlpL3d1ndmEQO5ypgilpBBLcEgFWm/dhxg0CUKdfOEpzZgMhMPU5BnDUG1ZuKukdZue+Hc4nWFIXXbe1OYSpF2iNd1FtySgYpcnsjEIYxAsOHCw1FrxMdAhSlJRb5ZUYafCS9RCqL/o5vMvE9hOpDZvSUakgCbtXdaJlyy9KoA82lwvt01x8uJDVWWDZxMXPeeK9O3Vcg7aLHonU+DMjxrnKExXKR4SM58M7vxLcGsyD8D4fWlU5skdH+2eEp9AsHPTm96DLMaJPu5urpHM/unL3zzedcz3wyY7f1TMG9hxkxC7RN4kQAAAABJRU5ErkJggg==","width":"35","height":"35","old_width":"35","old_height":"35"}],"currentFrame":0,"name":"Untitled","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMA/sfR5H8Fkddasdmnacvx//8745jkhasdASD945kjknhj/AAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAn9JREFUWEfNmFGOwyAMRCG5y+b+x0nvQlmZYjQMNmSjrpT+VGkbPDyPbdIYQvgJIbye8B7D5/UIQSLmEUJEx7+QyTnnSnx4izG6AG6TmQUUBZ+Y/Us0xhgPz6MumZzz6e1OP7cC4j01ePuoXq/JWDtdBZMok4wMdPS3XqoaGSHhoZ3kX5EXj0AwpsHXZqoaMiGDYnDHxk6GFLIQuv9SqkwystD7/Q77vssONLD8VgiUDaSUzn3fS5CZEDbzzMQDGVgYU1CEgCH1vtMTopT/YmKXTF2skWEhQmbbtkIGA8M1b4ApDlXVkdGFLAJg4iOndIZt66oFyralUlLMZDStVr/p+oyaGNFzjlkIlWsRIutUzxU/ccq8ftOh4vJelarxfREiu78gZihvl8ykzK0KOrRjg9fkWIKmb5SMIinD2iTj9Qj1DRHB8pef6HV5t9IEvuniX/LMpGKG8q+C2+cshtLXpcr1DC6SUgpSxkbvaP2miugaI5KRNfQlzdIy8UCGyrspJ09glbAgTVXnGSEiGxJRIMYnIybC8q4zSQ9Khza6CSE+SzfPqBglbpl4OM9oeeNNUFld1ZCRW8pTSll3r6RZjGXioSUTGU0zemEoVx6G2mdkE7oRTI9nYpcMVEWZ2tCJ3eEII6ObWXJRxZRyJ980IFfIoMmaMIsGi8FrrSZJn2filWc6Iatzy0rgqrxdMpoaNR4H8ubWLFWQrjZScHqbZCYd1zznwgjotFiCjZHie0aOlLPDOR+ksEnOPGOR5bP1QAYbH+8Cg115jPHEXZra+qTnkeHFcbqvqBhemp9n9E+A1aMrERqK4O6fCV9b6K4AvM/0zDcWvvMH1KPI/AIAOS/9S+9jJwAAAABJRU5ErkJggg==","palette_id":false} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d100.pixil b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d100.pixil new file mode 100644 index 000000000..c4fd81342 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d100.pixil @@ -0,0 +1 @@ +{"application":"pixil","version":"2.6.1","website":"pixilart.com","author":"https://www.pixilart.com","contact":"support@pixilart.com","width":"35","height":"35","colors":{"default":["000000","ffffff","f44336","E91E63","9C27B0","673AB7","3F51B5","2196F3","03A9F4","00BCD4","009688","4CAF50","8BC34A","CDDC39","FFEB3B","FFC107","FF9800","FF5722","795548","9E9E9E","607D8B","ffebee","ffcdd2","ef9a9a","e57373","ef5350","e53935","d32f2f","c62828","b71c1c","ff8a80","ff5252","ff1744","d50000","fce4ec","f8bbd0","f48fb1","f06292","ec407a","e91e63","d81b60","c2185b","ad1457","880e4f","ff80ab","ff4081","f50057","c51162","f3e5f5","e1bee7","ce93d8","ba68c8","ab47bc","9c27b0","8e24aa","7b1fa2","6a1b9a","4a148c","ea80fc","e040fb","d500f9","aa00ff","ede7f6","d1c4e9","b39ddb","9575cd","7e57c2","673ab7","5e35b1","512da8","4527a0","311b92","b388ff","7c4dff","651fff","6200ea","e8eaf6","c5cae9","9fa8da","7986cb","5c6bc0","3f51b5","3949ab","303f9f","283593","1a237e","8c9eff","536dfe","3d5afe","304ffe","e3f2fd","bbdefb","90caf9","64b5f6","42a5f5","2196f3","1e88e5","1976d2","1565c0","0d47a1","82b1ff","448aff","2979ff","2962ff","e1f5fe","b3e5fc","81d4fa","4fc3f7","29b6f6","03a9f4","039be5","0288d1","0277bd","01579b","80d8ff","40c4ff","00b0ff","0091ea","e0f7fa","b2ebf2","80deea","4dd0e1","26c6da","00bcd4","00acc1","0097a7","00838f","006064","84ffff","18ffff","00e5ff","00b8d4","e0f2f1","b2dfdb","80cbc4","4db6ac","26a69a","009688","00897b","00796b","00695c","004d40","a7ffeb","64ffda","1de9b6","00bfa5","e8f5e9","c8e6c9","a5d6a7","81c784","66bb6a","4caf50","43a047","388e3c","2e7d32","1b5e20","b9f6ca","69f0ae","00e676","00c853","f1f8e9","dcedc8","c5e1a5","aed581","9ccc65","8bc34a","7cb342","689f38","558b2f","33691e","ccff90","b2ff59","76ff03","64dd17","f9fbe7","f0f4c3","e6ee9c","dce775","d4e157","cddc39","c0ca33","afb42b","9e9d24","827717","f4ff81","eeff41","c6ff00","aeea00","fffde7","fff9c4","fff59d","fff176","ffee58","ffeb3b","fdd835","fbc02d","f9a825","f57f17","ffff8d","ffff00","ffea00","ffd600","fff8e1","ffecb3","ffe082","ffd54f","ffca28","ffc107","ffb300","ffa000","ff8f00","ff6f00","ffe57f","ffd740","ffc400","ffab00","fff3e0","ffe0b2","ffcc80","ffb74d","ffa726","ff9800","fb8c00","f57c00","ef6c00","e65100","ffd180","ffab40","ff9100","ff6d00","fbe9e7","ffccbc","ffab91","ff8a65","ff7043","ff5722","f4511e","e64a19","d84315","bf360c","ff9e80","ff6e40","ff3d00","dd2c00","efebe9","d7ccc8","bcaaa4","a1887f","8d6e63","795548","6d4c41","5d4037","4e342e","3e2723","fafafa","f5f5f5","eeeeee","e0e0e0","bdbdbd","9e9e9e","757575","616161","424242","212121","eceff1","cfd8dc","b0bec5","90a4ae","78909c","607d8b","546e7a","455a64","37474f","263238"],"simple":["ffffff","d4d4d4","a1a1a1","787878","545454","303030","000000","edc5c5","e68383","ff0000","de2424","ad3636","823737","592b2b","f5d2ee","eb8dd7","f700b9","bf1f97","9c277f","732761","4f2445","e2bcf7","bf79e8","9d00ff","8330ba","6d3096","502c69","351b47","c5c3f0","736feb","0905f7","2e2eb0","2d2d80","252554","090936","c7e2ed","6ac3e6","00bbff","279ac4","347c96","2d5b6b","103947","bbf0d9","6febb3","00ff88","2eb878","349166","2b694c","0c3d25","c2edc0","76ed70","0dff00","36c72c","408c3b","315c2e","144511","d6edbb","b5eb73","8cff00","89c93a","6f8f44","4b632a","2a400c","f1f2bf","eef069","ffff00","baba30","91913f","5e5e2b","3b3b09","ffdeb8","f2ae61","ff8400","c48037","85623d","573e25","3d2309","fcbbae","ff8066","ff2b00","cc553d","9c5b4e","61372e","36130b"],"common":["000000","FFFFFF","7F7F7F","a1a1a1","C3C3C3","c40424","880015","B97A57","dba88c","ED1C24","f75b63","f26f9b","FF7F27","f7ab79","FFC90E","FFF200","cfc532","EFE4B0","1ee656","0c6624","22B14C","B5E61D","5487ff","00A2E8","99D9EA","3F48CC","7f86e3","7092BE","720899","cd55cf","A349A4","C8BFE7","ffffff"],"skin tones":["ffe0bd","ffdbac","ffcd94","eac086","e0ac69","f1c27d","ffad60","c68642","8d5524","896347","765339","613D24","4C2D17","391E0B","351606","2D1304","180A01","090300"]},"frames":[{"name":"","speed":100,"layers":[{"id":0,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAExJREFUWEft1LERACAMAzGy/9DOBCncUYgJuI/OkyTvkzc+c1xCmYuoMsq088UMM8y0BZhpi9kZZphpCzDTFrMzzDDTFmCmLWZnrmIL7qWLmCXUhAcAAAAASUVORK5CYII=","edit":false,"name":"Background","opacity":"1","active":true,"unqid":"1exvb","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":1,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAArBJREFUWEfNWMF2wjAMa+n/fzJ0zyEyiiKn5W2H9bIBia3IsmLYt3/07L/AclZ7z/Pc9n3/Ova3G0oAfwHsFpjzPE86aOwJUNXfCVcwFc8VW5dgCEhL3kuwPZ/P7TgOAAKAnYFTudq+K0BLMMJICwYwCA4Ur9crGTmOA+sGsFeASjDmhAMYrUUwFU8AYdDu/4ohC6YCIgmx14oaDAI0v64YmsCoRhBMT87MxGePxwOaSGY6A7bRHKABjDLCUUITKEG8zwA0GzqPEvKS1JECUmYa5UoxRWqBHBMCaEro6AnQbJAJRsoTJ2/AiA13omzpfgjnRxOOSj/MzCREMMDObvylectQh89NwK2dgu8xcgvYUTCDsVHJMqgEGkyuR9+DVTDqEqv+BjBVBzkwRPGqtRNQrOfkWiLopnVePw3fNYOA2dJF3G4v31dtuTM9B67lITAAl/V3rfktM647XbdOYLjNIMrFbZ3gsQ9akWSsq9Sk1Y0wY93SeAhKkA0AK8DaLuAGRBOrbyV7WiZ2YXVZYSk1geDuPlo5d3wW10iaHwvYjQycKB3u7SOtY4SJ9hL3WPzPV0gkD8D83nCQThGcc3BZN8aKqBsqLZEBmNdIBYRbmzWghopxseoynfZUd5VhDoNajKTTdVC1c0NOy7Vj4CmrDmA3Nub3iV4MVGXZKq+owMRB3NzMY4Sb9PTCTG9gdpTBYnZJQaNryPUh1/zWMA1XSOjatCqVXhkFO9b8esx3e+pGdl62/qu273py44QttU55Fkxy1wVr6J8GMoCGQJkp3o91DkgJRgERe0MbayItY99X7Zmq4gScuauS8TdLPm23CvfVt8WsGEHCJZgFQww475aCmVtAlmViYbMl6xWh5uU6Lk9+8TPJJTMVqDss3AVxu0yFZ7x/jlg8Vz9/uK0/fGymIjAHw4EAAAAASUVORK5CYII=","edit":false,"name":"Layer 1","opacity":"1","active":false,"unqid":"oo532f","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":2,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAArJJREFUWEe1mFt2wjAMRDHsf8khPVItdTwaOaGn5QuSWL4ePcN4/PJznue5WzrGGJ+a/nhBbGAw3X7G+e8wAsAOwwot1z4Bu6VMA2EiGciA+/E97AYoX5cevIQhEKWEGfbrU4XcCH9HiO3ct4VBEJA7N4XNCiTGd8TWFVALI6R/HMfxeL1eRWKliMsFCRXP7IAkDMdIGDIY+yAQGF9glWpXQAVGuMaCIU+pygu6QX2PmPKIn7aUQguMAkED4B9cl6lN7sIsi6XLNQZiZTxVsXaQay6zCWPljopYh+QJ4xg3QZaA5mAOZSmg81CoDsO4jGH9/X6X7AnA5/OZEFA7cvmmNQXIAmQ2tsrgkQ3CAOYa1SSz6mKGiVLb1qRSto/jOOemWVkDREidfXN+KUAZuT/nLtU64efDeNKl10SZ34Dg4c9ZGEuzjPWdah5rBHPZY6gxIoivBZhopLJiLwvH+K4/QpmlyEGs+OFU48S+1WWg6lWLpJ0yaJBkVYXMIS3Wok1wD0NYTHWuSUoZN2wPmnGEEf5O19jzEfgIw4eBBMkyYkkt3WQXub5wb1LpjQAM0/Qr9xL2KldmN8+CXzNDKEh9fRyAO7tqshPuXtHjngK1sQBhrBj4FYxSNWKqVOAu2BQQ5azHwA0XZx2MhlxgcLLjiZ8Dl9I9U55AVM3CwdyTpGuUS+xw9+Xf2CLMqAKJzfBeY9c9VIarkvuzp4CRbqYplTYusOvx+tyvwkRmIRAZjAO0L26mAicA1quwje7BIConiqEEg5ZH0jQAU4ga2EH9ZTxFRbYwSqE4iZ08qjOPFhwPVKPQrHwX5xl4UQkV2myUryiboM/MUopcKgOx4vLyGCnglmapwHYgJZu6dMC5WL0liiBPUzQ6bD2xvclw/AfR7v8ZXHv3v5qPYNpC8kc3vgArjLsiNA/itgAAAABJRU5ErkJggg==","edit":false,"name":"Layer 2","opacity":"1","active":false,"unqid":"oo532f","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":3,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAqNJREFUWEell9uSgzAMQ6H9/0+m3bEbZ2RFduiWl90Bkhxk+dLz+Of1fr/f3dLzPM9vt/5qAQNU5zHnXbBbMAjBAHZw3AsIfAfBdlBbmADpVLBnCgTDBM/LM1sYBAEFbI0/Cgj468+O4+C/zrUDKmEKReIQ3rxTplqznC1hWBE7GcI019h7qM51Xcfz+XSw4Y9pN1Zx7JnOX2Cq0MTBKl0xhCLjpjKF2SdDgilAlpAEEPkmhUNAn/BB03eokAoTFzNeKOG6bLLwPR6PCDWeGYng95b4h+uHR6TE9M4UoSvKtt/wVGnoREnptxzCxYxM3bYH2ywMzh8ThmeYFJKgicxAr0CGbb0yak+CQaAEw8aqwiCU84+5rkuqYmk+LgdGZWR1Hje9au7SF5RyYRACDnYF4sL7r9fLaxDeS5kJMH4AAkUWEAQaPymCdSRUNAAGtcyyy55FlvlagkH1qv9xzfQch2oA+McxEGycsxVhoIx/OuHaBrAJhkKzVhgQtIMpqCp2DKiUKRthiu1niOtUdT9FeFT45FyEBo4U5B6CqU2jhFtDjAwJlPdDVVJLGUbz7gubzrBiBw5TwrsBw/6ayth6dTjuNVsJ7DK/sFOGaoUqBylEXFsYDqDSBL8ULmpwzi36iwOhNEWbmKFTgGYF1ShTVeUqGl8iMs0z8E6/quYaHiH8C9n9XBewGBaDeqofZcEag3yIsgxX/LMjNuoq6eLeIXgx4yygEqbLrOg3qq8opbphi0I9BVGTXoo9hoyA2lbSFbrqJ4uEIYWShyo/sXKiPHg0KxAs6dJjaqTAiomLzFNx8c+VuN+BbGGUhwJGzbM8gmD13oHcguGQFX2Iu3lS+g7IbRhqGVXZqEIdvy6360oDb1d+uvXPALjBLzA3eL975Q81aKYi/zu6FwAAAABJRU5ErkJggg==","edit":false,"name":"Layer 3","opacity":"1","active":false,"unqid":"oo532f","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":4,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAp5JREFUWEe9l8uWgzAMQ0v7/59M6bEbe2RFDtDFdDPTB/hGVuSwPX58HcdxrC7dtm27e+vLF3DxrhYz3oE6hUEIBrDC+Jl6H+pcgVrCBEhXcN/3x+v1sntkyxjIYEKtM6AWhkC8YKfEgJIWiWuuAEkYAeIL5Gpc6P1+m1IPg7PX8/n0v6HsGdAEo1pj9xsgCRQFrXi80LwIMNrj166ACgyC8I2jOBgyVz5gWTnZ2hVQwjSKuMr23Y+xUXyGnlMKFZjVrrH+w/e5g0yx8IaRh2+wdd19B1wy+D9ClVAji8KqJhD2BAKBAux/bKNzJIxaNcuKKwxFEISqlYXgrhKtb2HSo+gVBFM7KQJw3/cDdtgEhBsA4NfKsLwdDCnk9++AusGZtSI/uhinvIhiHm6jcP6PK+1UjWtoQ3j+ZJjhVrMbDROmybDnEf92jf3OXgNO+gRVhhbPMw2TVSWoyhmcRWpYRrCFuRuY2ECZzFPMr6I9kpYMjKHIUZDK8naXxxH0DBpskbi+ADBpkZtVgfd5e5VZxTMIgrI2obURjNooPBizLZC89Rw0yHP2dNRCNW4xDt0CAi3ittW2MgwFkTzDwM5qPRfG51nVZM3pOJi8wOYevirzzX5D2z3r81CFUVJhMEfwfNINOhWSi1UfaoRQFzL0fHLbStWZQ+0yguxU9M8bkLimHiHCN6hO6/hvb/6Cqj6rTSbmrS1OAjMMA1EbJMAdFZXq/OiCK/Fu8DmYzzk8vW0mKV9ha9hffMILG0wwJ0CycGdmHKg4p7qHOQnTeShXMD/TZ5hRO8phvlNkqQzkiRtVnWnImMvdBL9tFx+zoomH78djy2OxDDkhvcFPqXymyCVlkFI8Oyk1ymdXIW7DnIDl13cBSqAue/TPX34AQUDBIiqR4IkAAAAASUVORK5CYII=","edit":false,"name":"Layer 4","opacity":"1","active":true,"unqid":"oo532f","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}}],"active":true,"selectedLayer":1,"unqid":"vr50fd","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAjVJREFUWEfNWEFywkAMa+D/T4Z0nEEdRZHshR5aTiQhu7JsyV62fd/3r3/y2T4Fs22bDeE3sb0FRgHUxnzPXb9D+hKYtOHj8fi63W4toAKzytYIpoAg4sREgbrf7zFtWGNiqQXDi7gaYZC10fP5PEAVuPoUa/XBuxNDEYymhhet79iQGeHNFMAKIAtGaa1rbA6q6x4i1/ShTjjFnKLE0AWMU4yyMuU+gWHQDtAJzKpqeFEoCgBRN7ju5K+ALJiUe6WdgXBNMCCslTzqtBccmBebvqOAVS1aF1MRX54rGKZXZYmXnZLAkrKSVGTVmphRerVOCig8BQy5unFKUwH87FVgOgm6lLHjOvftAuEUXwTDYBhx0Y2+o4sDQN2v34ElJ111cXbnCEYbWio+RAbndc2S10pg6n0Ff0g79R12WzYyLmBmrWMmFfbJj1KaHECOGIunVpCU2LqzY0aj1XaAWgHVrj24NGuXtzXjouj6iNbBVG/KIoLhwezYrzO9rpYcINeZtVc5Fk8+MzETTeo1Bepzljs/06aKfS9gUsFNjc5F6nqUayGX1sNHFXViO3O8jihJ0qtAtJgPMvTc1JndZPNu9GApu+8Mvh07p1GiU9xUh5b1dKJ0KXPDFXsNq09d2o0MWm9LRxU7e9DxNo0J3FBVOdYop7O2G466EZLZczXWqW88Ub7bY5ya1KEToCUwCuikADK+5MquWD9Kk27MRpXSpWY2GSOeLzNzqfy//n9mNcJPf/cNbqwfxYQm5T8AAAAASUVORK5CYII=","width":"35","height":"35"}],"currentFrame":0,"name":"Untitled","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMA/sfR5H8Fkddasdmnacvx//8745jkhasdASD945kjknhj/AAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAjVJREFUWEfNWEFywkAMa+D/T4Z0nEEdRZHshR5aTiQhu7JsyV62fd/3r3/y2T4Fs22bDeE3sb0FRgHUxnzPXb9D+hKYtOHj8fi63W4toAKzytYIpoAg4sREgbrf7zFtWGNiqQXDi7gaYZC10fP5PEAVuPoUa/XBuxNDEYymhhet79iQGeHNFMAKIAtGaa1rbA6q6x4i1/ShTjjFnKLE0AWMU4yyMuU+gWHQDtAJzKpqeFEoCgBRN7ju5K+ALJiUe6WdgXBNMCCslTzqtBccmBebvqOAVS1aF1MRX54rGKZXZYmXnZLAkrKSVGTVmphRerVOCig8BQy5unFKUwH87FVgOgm6lLHjOvftAuEUXwTDYBhx0Y2+o4sDQN2v34ElJ111cXbnCEYbWio+RAbndc2S10pg6n0Ff0g79R12WzYyLmBmrWMmFfbJj1KaHECOGIunVpCU2LqzY0aj1XaAWgHVrj24NGuXtzXjouj6iNbBVG/KIoLhwezYrzO9rpYcINeZtVc5Fk8+MzETTeo1Bepzljs/06aKfS9gUsFNjc5F6nqUayGX1sNHFXViO3O8jihJ0qtAtJgPMvTc1JndZPNu9GApu+8Mvh07p1GiU9xUh5b1dKJ0KXPDFXsNq09d2o0MWm9LRxU7e9DxNo0J3FBVOdYop7O2G466EZLZczXWqW88Ub7bY5ya1KEToCUwCuikADK+5MquWD9Kk27MRpXSpWY2GSOeLzNzqfy//n9mNcJPf/cNbqwfxYQm5T8AAAAASUVORK5CYII=","palette_id":false} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d12.pixil b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d12.pixil new file mode 100644 index 000000000..70e25c5ae --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d12.pixil @@ -0,0 +1 @@ +{"application":"pixil","version":"2.6.1","website":"pixilart.com","author":"https://www.pixilart.com","contact":"support@pixilart.com","width":"35","height":"35","colors":{"default":["000000","ffffff","f44336","E91E63","9C27B0","673AB7","3F51B5","2196F3","03A9F4","00BCD4","009688","4CAF50","8BC34A","CDDC39","FFEB3B","FFC107","FF9800","FF5722","795548","9E9E9E","607D8B","ffebee","ffcdd2","ef9a9a","e57373","ef5350","e53935","d32f2f","c62828","b71c1c","ff8a80","ff5252","ff1744","d50000","fce4ec","f8bbd0","f48fb1","f06292","ec407a","e91e63","d81b60","c2185b","ad1457","880e4f","ff80ab","ff4081","f50057","c51162","f3e5f5","e1bee7","ce93d8","ba68c8","ab47bc","9c27b0","8e24aa","7b1fa2","6a1b9a","4a148c","ea80fc","e040fb","d500f9","aa00ff","ede7f6","d1c4e9","b39ddb","9575cd","7e57c2","673ab7","5e35b1","512da8","4527a0","311b92","b388ff","7c4dff","651fff","6200ea","e8eaf6","c5cae9","9fa8da","7986cb","5c6bc0","3f51b5","3949ab","303f9f","283593","1a237e","8c9eff","536dfe","3d5afe","304ffe","e3f2fd","bbdefb","90caf9","64b5f6","42a5f5","2196f3","1e88e5","1976d2","1565c0","0d47a1","82b1ff","448aff","2979ff","2962ff","e1f5fe","b3e5fc","81d4fa","4fc3f7","29b6f6","03a9f4","039be5","0288d1","0277bd","01579b","80d8ff","40c4ff","00b0ff","0091ea","e0f7fa","b2ebf2","80deea","4dd0e1","26c6da","00bcd4","00acc1","0097a7","00838f","006064","84ffff","18ffff","00e5ff","00b8d4","e0f2f1","b2dfdb","80cbc4","4db6ac","26a69a","009688","00897b","00796b","00695c","004d40","a7ffeb","64ffda","1de9b6","00bfa5","e8f5e9","c8e6c9","a5d6a7","81c784","66bb6a","4caf50","43a047","388e3c","2e7d32","1b5e20","b9f6ca","69f0ae","00e676","00c853","f1f8e9","dcedc8","c5e1a5","aed581","9ccc65","8bc34a","7cb342","689f38","558b2f","33691e","ccff90","b2ff59","76ff03","64dd17","f9fbe7","f0f4c3","e6ee9c","dce775","d4e157","cddc39","c0ca33","afb42b","9e9d24","827717","f4ff81","eeff41","c6ff00","aeea00","fffde7","fff9c4","fff59d","fff176","ffee58","ffeb3b","fdd835","fbc02d","f9a825","f57f17","ffff8d","ffff00","ffea00","ffd600","fff8e1","ffecb3","ffe082","ffd54f","ffca28","ffc107","ffb300","ffa000","ff8f00","ff6f00","ffe57f","ffd740","ffc400","ffab00","fff3e0","ffe0b2","ffcc80","ffb74d","ffa726","ff9800","fb8c00","f57c00","ef6c00","e65100","ffd180","ffab40","ff9100","ff6d00","fbe9e7","ffccbc","ffab91","ff8a65","ff7043","ff5722","f4511e","e64a19","d84315","bf360c","ff9e80","ff6e40","ff3d00","dd2c00","efebe9","d7ccc8","bcaaa4","a1887f","8d6e63","795548","6d4c41","5d4037","4e342e","3e2723","fafafa","f5f5f5","eeeeee","e0e0e0","bdbdbd","9e9e9e","757575","616161","424242","212121","eceff1","cfd8dc","b0bec5","90a4ae","78909c","607d8b","546e7a","455a64","37474f","263238"],"simple":["ffffff","d4d4d4","a1a1a1","787878","545454","303030","000000","edc5c5","e68383","ff0000","de2424","ad3636","823737","592b2b","f5d2ee","eb8dd7","f700b9","bf1f97","9c277f","732761","4f2445","e2bcf7","bf79e8","9d00ff","8330ba","6d3096","502c69","351b47","c5c3f0","736feb","0905f7","2e2eb0","2d2d80","252554","090936","c7e2ed","6ac3e6","00bbff","279ac4","347c96","2d5b6b","103947","bbf0d9","6febb3","00ff88","2eb878","349166","2b694c","0c3d25","c2edc0","76ed70","0dff00","36c72c","408c3b","315c2e","144511","d6edbb","b5eb73","8cff00","89c93a","6f8f44","4b632a","2a400c","f1f2bf","eef069","ffff00","baba30","91913f","5e5e2b","3b3b09","ffdeb8","f2ae61","ff8400","c48037","85623d","573e25","3d2309","fcbbae","ff8066","ff2b00","cc553d","9c5b4e","61372e","36130b"],"common":["000000","FFFFFF","7F7F7F","a1a1a1","C3C3C3","c40424","880015","B97A57","dba88c","ED1C24","f75b63","f26f9b","FF7F27","f7ab79","FFC90E","FFF200","cfc532","EFE4B0","1ee656","0c6624","22B14C","B5E61D","5487ff","00A2E8","99D9EA","3F48CC","7f86e3","7092BE","720899","cd55cf","A349A4","C8BFE7","ffffff"],"skin tones":["ffe0bd","ffdbac","ffcd94","eac086","e0ac69","f1c27d","ffad60","c68642","8d5524","896347","765339","613D24","4C2D17","391E0B","351606","2D1304","180A01","090300"]},"frames":[{"name":"","speed":100,"layers":[{"id":0,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAFpJREFUWEftlDEKAEAIw67/f3TvBQ4tCA5xVzSGyrbfkVK7jCRNN9Qz28YNmDUZltkgMM3kTZBJfcMZnMGZlADOpMTIGZzBmZQAzqTEyBmcwZmUAM6kxE7lzAeBa4+YB9sR7AAAAABJRU5ErkJggg==","edit":false,"name":"Background","opacity":"1","active":true,"unqid":"4anbk4","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":1,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAjZJREFUWEfNmEuSwzAIRMf3P7SnrBFU0zQIezXZJLEt8fi1SK6ff/S6vrLc931Xa6/r+rTveJEyrmwqxincCMZATsa7+xOgIwyDoOfbwJOuZ5/1jhE0OFtzAmphKpBtxAC4dNZ1AFj3J0AlzABk2cCoGIRF7C2QhKlqhDa3tdhVfo1BLHxdhBKMiggWZtPRqdNFzbQpCzAVyAPwUTrcOBTviqSKkMOI1KTOeBMVC9PjBDgTUstAAQa8907B3NPiUoGxxQEm1BOm0KKmYLBlQ/tyAReqXLY2SoJFa78vjgRDD3mIJyCQmgTEdfcGxhV1GwgiRirsXaTUmerFdektzJJRyu2K5uSsQuFTe6CTZc0g8bK8u8E+CxiMIkZQpgq7zFq8hVELGpggdtxxlRLbMdIWMAmSixPApFRhXRxOedScJIiVzkhvGYj1vwMpUm5wfWsLQ35JzSkTENxz1E18umI0VEtjzvdIEVJL0UyH6VH0uIDZI3E/HR/FHmnsGBWwGhs4PcJrOXaC0tt5FuBla2ORKcOHlJ1AUhmWOrNvuOJyi3ff2Qqd/mspPKOmwdhNBqO859O2ilDR+pwamaK1VmkF10t1tuDZldrk70I4KuB7P+lBSy9PKqD9nJ87NMmhEYQJn6uhfPzrQE15PPuAQyFQRWcm2xJG1Q8WMGtIBYVEYtZJmS1hEIjTpoyIUWM9NoFw54rCC5fV72c0NPhDoHX6FQwX9yRS+5kRxCcYBaUie/q3ocrGL7FUikLdy+7AAAAAAElFTkSuQmCC","edit":false,"name":"Layer 1","opacity":"1","active":false,"unqid":"a2sj1t","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":2,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAi9JREFUWEfVV9FuAzEI6/3/R98UFpBxDEmr09Ttpes1CY4xhrteX/R3fRGW1/8Dc9/37Qxe10f4jzZtFzkQBDGwje/+OYAC3sj89bvJL7KNtV0Ah6WADG4gmM/8TARxBKgEMxipUuIs+O/IirOBrA2cJwx1zIROOLinhcHCOg9un2K/jFuBWYCwgFkvBOxyZhGI2JPiL2AwPUR1aEbppao2SmGytXl+YEhgGiCSblVBGK3THOpt6iybXkftRrTLjdnZC7HbMmeI02RaAfriO5RuVeJ4Vtrne1k/+Hyw04GxtRNclHmhARMspSgqChkHL7ItYJ4JTKogwBkecQoExBxaw6D+O4HMYCp794OK8o4yhiDYEqJtcPrZDFOelblx32Ehiv6z6Ez0ssS2p64VnRLevHJpCcwONlT2rcEM2InWDJelsHnDOQ/ivvM8M5XLYloKkEvpc9VQ04yOHnSL8mvnFWViwuhkNTmjmKLwEThk26mV3W8Gq9Q0lc+4n73Vm7iy0JX9QLwY+1LlwBLMPIhbApKx2DwBirXMVtebSjBKO1TOEhDrpQIJvS88dPmHDsM+o0bGElA1/bF7K7+Skx6pfATGMdIup8wNLhSNE6yA28YS+2QGVqaGzxad8AgqqvWtGdimaOrcQQY2uGLGTQP5E28HO0AGrhiyu3FEad2edWnyYO37k5h70qBVsCsBbcHM29tNP3zPxjG2ZOWImXb3wz8eMfNwzM8181dARpwfC5kXQnaygo4AAAAASUVORK5CYII=","edit":false,"name":"Layer 2","opacity":"1","active":false,"unqid":"a2sj1t","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":3,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAjxJREFUWEfNV0GSwzAIa/7/aO/YaxghC3A7PXRP3YSAIiRwntcP/T0/hOX1MZgxxnie9PGP8r79UAZijLFIRoBw7arOVdAswiCwuAKC7Z/3n4JGi23BVO3YRSZQZ8SAeYHYyrJeejNjQhVRoGbHZg7VuowlCYaAzJh/QbyW4K1GYGN2ARjCZwJzkOcw8gEmAWLJQxHORtpYoE0q+Htr8NBRANMAMWZWLkuIgLAwtCdlc8c4hgyMXbf2tEBYS+ywzPqoH24TakMN54ORncx1hILNnEWC18ywUElsKRBDjcWTlq1Q0k8L5mhLpRFzGceoOScAX4EJbSqG2UqG4q8Y4rmzX8TnBhYNdlT0K01M3fCAU4CokNm+ZkbtncItanAqfSlDoByOI4RbGRXfAHE2aUtLQMLiJTPHlKUEcudwS9ny7C4ailozyhENmLAqmJ3sqEF7LAXj0zfZLW8zQ/uJ3Ze3SWxe3y07qYNhm7KDmNHdyqmllcPyuSZJ4oe1WcgKENtE6YXPPFdtYma4VSi8sJBsd/yjPc48sFrC2agcerQ7Qn+VdZPhEXSV2PlYyt15xib9mpa8ErIphtf5OWshMOXh2Rk4oG6+kVpMrBcFJIxikTFMUDW0OhT8DQX/SxLSrwOwodcsrMruzARcEtCBWZrBqYzWhjmxZseODUOTZlFZ7wZMCghOhkGzxefMV8AcgPYbh72EH27IyBZw++JtAE9oFu3lIeqqzlVQ4Rr8lFlh4nh6XeM6sLPxN+7/AbQmAUIHBi45AAAAAElFTkSuQmCC","edit":false,"name":"Layer 3","opacity":"1","active":false,"unqid":"a2sj1t","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":4,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAjJJREFUWEftV9FuxDAIu/z/R2dqLlDjGJKe9nCTtpdNbQaOMYa21xf9tC/C8vqbYHrvvbXPsPfeRwHaJsCT6COiBYbg1/Mrjr/Pcl7/WwE6BZMlasjYTIYyxPgYQ+Y9AfPm+L69sTB+GwAohYHBc/asBLQDg0BCQCxZUpYAmi5klwvNXIHhWwxtDCpuMRpY1xIBs/ioK2Q05M/ALEmce+ooFLQ4g2Cw1FhKZ0eBOeqKnVkCQwxosHi95+5iMKp9R15mQOmkOBPykGf5uwUMdoX6e+pFCXC5CJwNJUILQHYkmHnrpXWnYQUhB1OZeqpYFBbgF8vAmJm5qcFtvKOUbqx8GSDBimtncUi0dmtlEBuPg0AMtL13iz2jkikrCH3qbooGRcqXQWwuAXifVeRNVRNKMBiI2zAzt6VNZ9YgamNY6Wz4KLz4NWawJDTlAzMk5ghGzQ/zBB6ISgNXt/EUR1dWwLAxnBlKmj0fGFCURHnYa5LxkK4jZVJeESqfgXey48j6lSTuHRiZsRJsHJhdeDtKcF6pkbAww3MoMzHWzO7/lFfxsEwHmFghXR5qbajGAo4XA42XtL14WSHUV8BJewqxSk/KhqSavCOmWrJZPyjY5LYWK3hLsgmOM0c7MJvTtH9cQRczw6TVpA7lrYYFWnrybRQ8h8X+9Ptpx4xh5c8VZNU9ULEhui7NeQpmyMLGhXJg6g4fsH6bg0/cJ2AQ0EF130eoE8t8T8Ecg/jk4D+YjLUfNgMXQvU7QCMAAAAASUVORK5CYII=","edit":false,"name":"Layer 4","opacity":"1","active":true,"unqid":"a2sj1t","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}}],"active":true,"selectedLayer":4,"unqid":"d602vs","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAadJREFUWEftV9ESwjAIs///0fPmyY5lSWC9TX3QF7XtSggQ2FiWZXn8yGfMghljDOXD9J2zD95B5ilmDBkWWzcT2mACCF68rq9reV+BrkC1wChDcXm1v9KmnMmUlmDQULDAvtFosBYGK0AWDHs4hwCZyV4i2HUPw4mJJsEgEPQysxCG3JpisgwT8x6pjv8sKZkjyEx2YLsbdeaKqmAJiw5SpjMYlyOspDHm6kyHvZcDCCYnJfsdXncNM6ZVIlMwWchy4ikgVelWFbg5rZjZZXlS2VJF3/3TMce0axcmRWdmQ4kYVoZrHViFO7vBjOoxXaXtAHKN9mUHwTDhwhzCM0wzVHnb8H2TGazWHTOqfzBmlPQzRlgV0gb8iWqqtGYjgYG5UmeUqjPhm1bgM9XDhjCa4LO9qaooNYJkrTro1h1d2wFRRXJolJ2ZBYXLdXM2CbpprzXpKWrZjOI8d3IgmVFduKM3CCYnupsgSzCY8arbOvC5+2OTxL3yVcUBQmVVnleMbM5037XVWMBYwQlANdIpZlSVsRDgWjWM5fOtMHWMXnHmD0ax+AT6o7S2HI0/tgAAAABJRU5ErkJggg==","width":"35","height":"35"}],"currentFrame":0,"name":"Untitled","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMA/sfR5H8Fkddasdmnacvx//8745jkhasdASD945kjknhj/AAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAbFJREFUWEftV1sSgzAITC7T+5+nl7GjLRlcdwEz9vFRf2wTDcsCC/ZlWZb2I1efBdN77621W2vtjvfpM2dffAeZp5h5knH+qmZCGYwBwYPX9XXN7yvQGagSGGXIDs/2Vy6VM57nFAwaMhbYHY0aa2YwAxSCYS/7ECAz3ksEu+5hODH7JBgEgl56FsxQtKaYTMPEvEeq7T9LSuYIMuMdGGejzlxRFSxh0UHKtAcT5QgraYy5eqbC3uYAgvFJyX6b11XDjGmVyBSMFzKfeApIVrpZBQ6nFTO7LHcqm6roq2VEzDHt2oVJ0enZUCKGlRG1DqzCnV1jRvWYqtJWAEWNdrODYJhwYQ7hM0wzVHmH4fsmM1itO2ZU/2DMKOlnjLAqpA34E9WUac0ggYG5UmeUqjPhm1bgM9XDhjCa4LO9KasoNYJ4rTro1ju6dgREFcmhUVZmFhSuqJuzSTCa9kqTnqKWzSiR55EcSGZUF67oDYLxiR5NkCkYzHjVbSPwvvtjk8S99FMlAoTKqjzPGBnOVL+11VjAWMEJQDXSKWZUlbEQ4Fo2jPnnS2GqGL3imT8YxeIDSbO3tqGpQPgAAAAASUVORK5CYII=","palette_id":false} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d20.pixil b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d20.pixil new file mode 100644 index 000000000..4e2baeb61 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d20.pixil @@ -0,0 +1 @@ +{"application":"pixil","version":"2.6.1","website":"pixilart.com","author":"https://www.pixilart.com","contact":"support@pixilart.com","width":"35","height":"35","colors":{"default":["000000","ffffff","f44336","E91E63","9C27B0","673AB7","3F51B5","2196F3","03A9F4","00BCD4","009688","4CAF50","8BC34A","CDDC39","FFEB3B","FFC107","FF9800","FF5722","795548","9E9E9E","607D8B","ffebee","ffcdd2","ef9a9a","e57373","ef5350","e53935","d32f2f","c62828","b71c1c","ff8a80","ff5252","ff1744","d50000","fce4ec","f8bbd0","f48fb1","f06292","ec407a","e91e63","d81b60","c2185b","ad1457","880e4f","ff80ab","ff4081","f50057","c51162","f3e5f5","e1bee7","ce93d8","ba68c8","ab47bc","9c27b0","8e24aa","7b1fa2","6a1b9a","4a148c","ea80fc","e040fb","d500f9","aa00ff","ede7f6","d1c4e9","b39ddb","9575cd","7e57c2","673ab7","5e35b1","512da8","4527a0","311b92","b388ff","7c4dff","651fff","6200ea","e8eaf6","c5cae9","9fa8da","7986cb","5c6bc0","3f51b5","3949ab","303f9f","283593","1a237e","8c9eff","536dfe","3d5afe","304ffe","e3f2fd","bbdefb","90caf9","64b5f6","42a5f5","2196f3","1e88e5","1976d2","1565c0","0d47a1","82b1ff","448aff","2979ff","2962ff","e1f5fe","b3e5fc","81d4fa","4fc3f7","29b6f6","03a9f4","039be5","0288d1","0277bd","01579b","80d8ff","40c4ff","00b0ff","0091ea","e0f7fa","b2ebf2","80deea","4dd0e1","26c6da","00bcd4","00acc1","0097a7","00838f","006064","84ffff","18ffff","00e5ff","00b8d4","e0f2f1","b2dfdb","80cbc4","4db6ac","26a69a","009688","00897b","00796b","00695c","004d40","a7ffeb","64ffda","1de9b6","00bfa5","e8f5e9","c8e6c9","a5d6a7","81c784","66bb6a","4caf50","43a047","388e3c","2e7d32","1b5e20","b9f6ca","69f0ae","00e676","00c853","f1f8e9","dcedc8","c5e1a5","aed581","9ccc65","8bc34a","7cb342","689f38","558b2f","33691e","ccff90","b2ff59","76ff03","64dd17","f9fbe7","f0f4c3","e6ee9c","dce775","d4e157","cddc39","c0ca33","afb42b","9e9d24","827717","f4ff81","eeff41","c6ff00","aeea00","fffde7","fff9c4","fff59d","fff176","ffee58","ffeb3b","fdd835","fbc02d","f9a825","f57f17","ffff8d","ffff00","ffea00","ffd600","fff8e1","ffecb3","ffe082","ffd54f","ffca28","ffc107","ffb300","ffa000","ff8f00","ff6f00","ffe57f","ffd740","ffc400","ffab00","fff3e0","ffe0b2","ffcc80","ffb74d","ffa726","ff9800","fb8c00","f57c00","ef6c00","e65100","ffd180","ffab40","ff9100","ff6d00","fbe9e7","ffccbc","ffab91","ff8a65","ff7043","ff5722","f4511e","e64a19","d84315","bf360c","ff9e80","ff6e40","ff3d00","dd2c00","efebe9","d7ccc8","bcaaa4","a1887f","8d6e63","795548","6d4c41","5d4037","4e342e","3e2723","fafafa","f5f5f5","eeeeee","e0e0e0","bdbdbd","9e9e9e","757575","616161","424242","212121","eceff1","cfd8dc","b0bec5","90a4ae","78909c","607d8b","546e7a","455a64","37474f","263238"],"simple":["ffffff","d4d4d4","a1a1a1","787878","545454","303030","000000","edc5c5","e68383","ff0000","de2424","ad3636","823737","592b2b","f5d2ee","eb8dd7","f700b9","bf1f97","9c277f","732761","4f2445","e2bcf7","bf79e8","9d00ff","8330ba","6d3096","502c69","351b47","c5c3f0","736feb","0905f7","2e2eb0","2d2d80","252554","090936","c7e2ed","6ac3e6","00bbff","279ac4","347c96","2d5b6b","103947","bbf0d9","6febb3","00ff88","2eb878","349166","2b694c","0c3d25","c2edc0","76ed70","0dff00","36c72c","408c3b","315c2e","144511","d6edbb","b5eb73","8cff00","89c93a","6f8f44","4b632a","2a400c","f1f2bf","eef069","ffff00","baba30","91913f","5e5e2b","3b3b09","ffdeb8","f2ae61","ff8400","c48037","85623d","573e25","3d2309","fcbbae","ff8066","ff2b00","cc553d","9c5b4e","61372e","36130b"],"common":["000000","FFFFFF","7F7F7F","a1a1a1","C3C3C3","c40424","880015","B97A57","dba88c","ED1C24","f75b63","f26f9b","FF7F27","f7ab79","FFC90E","FFF200","cfc532","EFE4B0","1ee656","0c6624","22B14C","B5E61D","5487ff","00A2E8","99D9EA","3F48CC","7f86e3","7092BE","720899","cd55cf","A349A4","C8BFE7","ffffff"],"skin tones":["ffe0bd","ffdbac","ffcd94","eac086","e0ac69","f1c27d","ffad60","c68642","8d5524","896347","765339","613D24","4C2D17","391E0B","351606","2D1304","180A01","090300"]},"frames":[{"name":"","speed":100,"layers":[{"id":0,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAExJREFUWEft1LERACAMAzGy/9DOBCncUYgJuI/OkyTvkzc+c1xCmYuoMsq088UMM8y0BZhpi9kZZphpCzDTFrMzzDDTFmCmLWZnrmIL7qWLmCXUhAcAAAAASUVORK5CYII=","edit":false,"name":"Background","opacity":"1","active":true,"unqid":"epa8u","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":1,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAjNJREFUWEfNWNFy5CAMK///0WlhYo8QkoHcy+1LO9mAZVmyYdvPf/Rp/4LleZ6H17fWPu/5aSGCwNiI7QuoKzAOBLPzFdQRmB2ICM4VugVVgtmB6IywbJRkTkFJMDtNRMAe5P2/75PLnIZ3oCYwOxCdCQFkkowo2QCKLzlQCSaAKHf0Z8BClgZZ6TiRHQUc98ASh/MmMJh1ZKKAwLMpa8p4SYD3CkAlGBamKo3od1O5qjXMvgXDVEYEztp1blVOLBnuRwyPOo9P10x8qRar4Ce2rtYdgwlAkSm4pKmZVM04Z4ojzaA9mXY3i1DwXFqhkcT+7j8qJMuEqlfAkCXqIcmac+GVZoC60NL4y+5wQu+vov6UDgXbNTPORagh1fSw+VECS0/aChiZqUpUOG4plTLD21uGF8o+w4tflib6UbA0MLNNqJIbLfkyUdZyZKD40dY440jo2NJSh1tmXK2N5XHyB+0p/ihHsBvuI+3trS0YymCQkX0G61O8bIAjZjBYOERphw5SHBQdlEcM7mPbQYmIidpFyNxLWNDQGKOkis21A4tWPjagw5d1FjsohK4Ob0ezSTU87is8QLEdkFZCzONUwB+rmaKRTaMBN6z6RoyFBcH7gK0vD+TudE/9hHsFn40dhikxvHkuvN3cEMpo5ks6MU7x7SWuElzEOWHQsCnjfr5Rnp6JwzGQgI1ZggFX5SWM2cARoZxyAiLfuan7yd37hgmOfcSMyHj5kWjZ+FZQfxv8AsTOxkLIDDsGAAAAAElFTkSuQmCC","edit":false,"name":"Layer 1","opacity":"1","active":false,"unqid":"c2f8it","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":2,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAiFJREFUWEfVV0FuwzAMa/7/6Aw2LIGmSMUbcuh6aZHFNk2RlHZ9vuhzfRGWz/8Bc9/3PZi7rncwr+3GfnJDe4oCEptxacfe42/8He/h2eu934N5YgTBPQFBsIvtAkgiPGHFAWWA42AozyTLsVPAdECi1vgOH6TcqRhU7EgwfGu6yRT10PX6DmwFR+zDenLsbGCcewDMADLWIKD5W5UH0TmBo7MKGL6NqjkAmutdaVHUJ9pJMLxhc5MUobMuPlfijezi3NnAICu4ADQkmTAlmuVs9FKcZcGIcl0PTsPNMwSVbvhZOAs1swkx6o0LBXMJkN8LRljcwbgAufWIBKPKRQIMR00wFAUbQBBxuhCBos03ZlyANZqYe0HuSJvD4RbQ7MdAV4otnnFjJB2lmFVIsqMce2iOopkSoz7QMoH/ygzpp2qmU38kMeiEAc39VWbhwa5xtsw0EY8HyhTnkYEZVzOOzBlaWNyhNIVR4NpAl9ijRx2BAceU2YQsn/dg+4KFt7viRGB7E95+gY6OnY2RA0y5T7UDZjZIsV2bS4UREO7BQYY1EEBFdnHWJIajeQYHKbJxNkOlBy6NahF2nlmLOd5ZJ3K4UpY+iQl8R46drAW4IQORgxXb+GTKm2eaxHX/vMkxQh1+EnIlexQYVy53KIrVdXznoLZMsKiww9aF2+coYUTK+SMrIh92gByTJ8955j0u08nmb7/TMvP2YU/7fRWYH/oqWEIZZe9qAAAAAElFTkSuQmCC","edit":false,"name":"Layer 2","opacity":"1","active":false,"unqid":"c2f8it","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":3,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAhpJREFUWEfNV9uuwyAMW///ozuBSGSMDeE8HG0v2yraGF8S+nx+6PP8EJbPFZj3fd8A/zz+Vlj2eXYLiYkSmBOIKM51b0FtwZxA4Mao8LTnKigLJoBcsNwBVEA56RYwN2zszO9A7ViawNywgT5pvxuDzjsKNKxNDAuYU0qC4gY81jIYLF59XruHZcroxgNHIVyXSiIb/WGDHQSAssQal+gtGEFlBxtMKMMCeATd6xgbaJli/dhZLAq28r/yiPDQg1KGCnENNnQGQ4sTkCiQ0iLI4S3cSPudAP8KJrw1ecU92Ow85RXeOjMzTNYWSlbUzlBmEfNJtjIz4PpEjeZTPYISgkafkoimJ2/qaJP22eapr1hDkxTJrmmOXiblBU7KKIYSJjE8BlzyoBeVPdPrqm47otoTMnzVv5nV3Yy6kon13UR3YoY7MZsZmyZOgaUD08Kl29JAZKk6ixxfN1RLzHDLFwlYzi5BDc8lN0xLnrkpvDvPtA68Y0m1DymTma6ydmEqL/eRh67SJI+S7C2u6FJER5MYNf2yPULsEsBsMBD2iTpqwD3bpjd1XD67qAMVPlgMykykYat27HRjgZKzND30nBkBuGEPRkV0XJtmjDM5M+kiD9etTIH4+BorEpGjoNIe1LsTG3jy4ukdqhpr8oqtuQUDMbRMiUNUbqgKIiXbdVHRO47yiRiXNqz6TAmbk++WiaU/lar/06IvOzpSQih7Kx4AAAAASUVORK5CYII=","edit":false,"name":"Layer 3","opacity":"1","active":false,"unqid":"c2f8it","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":4,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAidJREFUWEfVV9GOwzAMWv//o3uKVWeYgNNKfdjt5XTr4hDAOD0+P/Q5fgjL5/+AOc/zHMwdxzuYr3KjnizY7jLA4DooFuqO/8fz6++olfiL+qbGsrcF41jpACWCHZMAvoJ2BkZWFICdfCRJyJ0fJ5dkBlkhKWzziQ0SwMFyp8TXgSYGC4Z1VtSb04d3LtTFR1yD5VrAsFeSGTSsohxkSzAMKkp0ckkwcIJZEH3DHkLagXpkZ9qFdUZ2Cpiug6CFo5Xxo1qX/DAWbL2zgDG6ls1ROjKj6piCm6UC+b9JqFhxDGCHpVfwtxumKq3f8KxgXBGWRCQvMpIjBA1cpHXTYMr0JOSaQMt6QfSYQWqkbMGklqJTZlakiZMG1XXDqNfzYMjJJ9KzTM+y+JqsU1/2CeRKMW1OZMW0OMQctnE5AITLxpyibzNDKvQGRhnypET7YlJgNPyCDLqJn4eWBp5xacJNSQYSWBAok/JmAdP4QIYezy1ez13n5lx2nk1gtRC+W2aWkWPpLPRd2sCC4UTFBQ/TNtjkAYszjp6vF+Nd+HExLggZIyc+zKIJNuOgvc9Q7C85ZQJtuVzdmdjBukjCuOKTnhhOyxWCwqz4JPdwV098bbl1B1bJyYcwl6+o390ItmCygDIvekRFwS6jjMdi2a33JpUn2FnNmFAuwJe+8tyCYXYUI2xwvowpeSkIn4HppJDHbr7sgLQyPd3ojd+3Mr2xwZMaPwXmDyLsWEINgQlHAAAAAElFTkSuQmCC","edit":false,"name":"Layer 4","opacity":"1","active":true,"unqid":"c2f8it","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}}],"active":true,"selectedLayer":"0","unqid":"unboeg","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAbJJREFUWEftV8tyg0AM6/7/R9OhE2eMVg9TesihuSRhwCvLsmzWcRzH14d81j8YUYkRM2utPylkUkQE04FUsLp2/j9/9+vsQBaDZXcLzBkgATrvSUwqhiyYzkA/BAGxLPuBCO7XYHopXMZ4gEqkgNNyqtbGOjMgLPsCXyVFTXUWtwQSGBZcBayD+zfTmWKHaga7BQMyzaQyKWFf2GXMMMFh1p2d1LrTUm3MKCBYGuc/qjS95KzUFoyj3pmdY8o1wgVMorvqjs6rZoWKpwBRMMxlmemhKNVYmNrEG4xzWyXe1HVdO2lM/LBd3YRgWNZdJyz4pL1d3A1Md050UQUGjXFSUuZdVsCsdCp7ZYxuwG7u3k0vTVfUSFyWXksZS4CuIujAd0zPlYeJV825NzAFRk1fNQSVTvA6Gw0STDc2VW+1OKmVgpkl7V43KGldXztvsvzUYWMwLhO3yzhbGPnSdLlimeJMcnp4BAa1M7Fz9kwHnAbxo7cDB3Bijhuz6V07tTJbJ9iKoLR2YW4Cph7A1u1AcG7dKU/dG98ok1aQavc/jo/EzJ3Dnt47YubpIdPnPwrMN5+zjLYb9TyVAAAAAElFTkSuQmCC","width":"35","height":"35"}],"currentFrame":0,"name":"Untitled","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMA/sfR5H8Fkddasdmnacvx//8745jkhasdASD945kjknhj/AAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAbJJREFUWEftV8tyg0AM6/7/R9OhE2eMVg9TesihuSRhwCvLsmzWcRzH14d81j8YUYkRM2utPylkUkQE04FUsLp2/j9/9+vsQBaDZXcLzBkgATrvSUwqhiyYzkA/BAGxLPuBCO7XYHopXMZ4gEqkgNNyqtbGOjMgLPsCXyVFTXUWtwQSGBZcBayD+zfTmWKHaga7BQMyzaQyKWFf2GXMMMFh1p2d1LrTUm3MKCBYGuc/qjS95KzUFoyj3pmdY8o1wgVMorvqjs6rZoWKpwBRMMxlmemhKNVYmNrEG4xzWyXe1HVdO2lM/LBd3YRgWNZdJyz4pL1d3A1Md050UQUGjXFSUuZdVsCsdCp7ZYxuwG7u3k0vTVfUSFyWXksZS4CuIujAd0zPlYeJV825NzAFRk1fNQSVTvA6Gw0STDc2VW+1OKmVgpkl7V43KGldXztvsvzUYWMwLhO3yzhbGPnSdLlimeJMcnp4BAa1M7Fz9kwHnAbxo7cDB3Bijhuz6V07tTJbJ9iKoLR2YW4Cph7A1u1AcG7dKU/dG98ok1aQavc/jo/EzJ3Dnt47YubpIdPnPwrMN5+zjLYb9TyVAAAAAElFTkSuQmCC","palette_id":false} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d6.pixil b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d6.pixil new file mode 100644 index 000000000..4d9d44d7b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d6.pixil @@ -0,0 +1 @@ +{"application":"pixil","version":"2.6.1","website":"pixilart.com","author":"https://www.pixilart.com","contact":"support@pixilart.com","width":"35","height":"35","colors":{"default":["000000","ffffff","f44336","E91E63","9C27B0","673AB7","3F51B5","2196F3","03A9F4","00BCD4","009688","4CAF50","8BC34A","CDDC39","FFEB3B","FFC107","FF9800","FF5722","795548","9E9E9E","607D8B","ffebee","ffcdd2","ef9a9a","e57373","ef5350","e53935","d32f2f","c62828","b71c1c","ff8a80","ff5252","ff1744","d50000","fce4ec","f8bbd0","f48fb1","f06292","ec407a","e91e63","d81b60","c2185b","ad1457","880e4f","ff80ab","ff4081","f50057","c51162","f3e5f5","e1bee7","ce93d8","ba68c8","ab47bc","9c27b0","8e24aa","7b1fa2","6a1b9a","4a148c","ea80fc","e040fb","d500f9","aa00ff","ede7f6","d1c4e9","b39ddb","9575cd","7e57c2","673ab7","5e35b1","512da8","4527a0","311b92","b388ff","7c4dff","651fff","6200ea","e8eaf6","c5cae9","9fa8da","7986cb","5c6bc0","3f51b5","3949ab","303f9f","283593","1a237e","8c9eff","536dfe","3d5afe","304ffe","e3f2fd","bbdefb","90caf9","64b5f6","42a5f5","2196f3","1e88e5","1976d2","1565c0","0d47a1","82b1ff","448aff","2979ff","2962ff","e1f5fe","b3e5fc","81d4fa","4fc3f7","29b6f6","03a9f4","039be5","0288d1","0277bd","01579b","80d8ff","40c4ff","00b0ff","0091ea","e0f7fa","b2ebf2","80deea","4dd0e1","26c6da","00bcd4","00acc1","0097a7","00838f","006064","84ffff","18ffff","00e5ff","00b8d4","e0f2f1","b2dfdb","80cbc4","4db6ac","26a69a","009688","00897b","00796b","00695c","004d40","a7ffeb","64ffda","1de9b6","00bfa5","e8f5e9","c8e6c9","a5d6a7","81c784","66bb6a","4caf50","43a047","388e3c","2e7d32","1b5e20","b9f6ca","69f0ae","00e676","00c853","f1f8e9","dcedc8","c5e1a5","aed581","9ccc65","8bc34a","7cb342","689f38","558b2f","33691e","ccff90","b2ff59","76ff03","64dd17","f9fbe7","f0f4c3","e6ee9c","dce775","d4e157","cddc39","c0ca33","afb42b","9e9d24","827717","f4ff81","eeff41","c6ff00","aeea00","fffde7","fff9c4","fff59d","fff176","ffee58","ffeb3b","fdd835","fbc02d","f9a825","f57f17","ffff8d","ffff00","ffea00","ffd600","fff8e1","ffecb3","ffe082","ffd54f","ffca28","ffc107","ffb300","ffa000","ff8f00","ff6f00","ffe57f","ffd740","ffc400","ffab00","fff3e0","ffe0b2","ffcc80","ffb74d","ffa726","ff9800","fb8c00","f57c00","ef6c00","e65100","ffd180","ffab40","ff9100","ff6d00","fbe9e7","ffccbc","ffab91","ff8a65","ff7043","ff5722","f4511e","e64a19","d84315","bf360c","ff9e80","ff6e40","ff3d00","dd2c00","efebe9","d7ccc8","bcaaa4","a1887f","8d6e63","795548","6d4c41","5d4037","4e342e","3e2723","fafafa","f5f5f5","eeeeee","e0e0e0","bdbdbd","9e9e9e","757575","616161","424242","212121","eceff1","cfd8dc","b0bec5","90a4ae","78909c","607d8b","546e7a","455a64","37474f","263238"],"simple":["ffffff","d4d4d4","a1a1a1","787878","545454","303030","000000","edc5c5","e68383","ff0000","de2424","ad3636","823737","592b2b","f5d2ee","eb8dd7","f700b9","bf1f97","9c277f","732761","4f2445","e2bcf7","bf79e8","9d00ff","8330ba","6d3096","502c69","351b47","c5c3f0","736feb","0905f7","2e2eb0","2d2d80","252554","090936","c7e2ed","6ac3e6","00bbff","279ac4","347c96","2d5b6b","103947","bbf0d9","6febb3","00ff88","2eb878","349166","2b694c","0c3d25","c2edc0","76ed70","0dff00","36c72c","408c3b","315c2e","144511","d6edbb","b5eb73","8cff00","89c93a","6f8f44","4b632a","2a400c","f1f2bf","eef069","ffff00","baba30","91913f","5e5e2b","3b3b09","ffdeb8","f2ae61","ff8400","c48037","85623d","573e25","3d2309","fcbbae","ff8066","ff2b00","cc553d","9c5b4e","61372e","36130b"],"common":["000000","FFFFFF","7F7F7F","a1a1a1","C3C3C3","c40424","880015","B97A57","dba88c","ED1C24","f75b63","f26f9b","FF7F27","f7ab79","FFC90E","FFF200","cfc532","EFE4B0","1ee656","0c6624","22B14C","B5E61D","5487ff","00A2E8","99D9EA","3F48CC","7f86e3","7092BE","720899","cd55cf","A349A4","C8BFE7","ffffff"],"skin tones":["ffe0bd","ffdbac","ffcd94","eac086","e0ac69","f1c27d","ffad60","c68642","8d5524","896347","765339","613D24","4C2D17","391E0B","351606","2D1304","180A01","090300"]},"frames":[{"name":"","speed":100,"layers":[{"id":0,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAExJREFUWEft1LERACAMAzGy/9DOBCncUYgJuI/OkyTvkzc+c1xCmYuoMsq088UMM8y0BZhpi9kZZphpCzDTFrMzzDDTFmCmLWZnrmIL7qWLmCXUhAcAAAAASUVORK5CYII=","edit":false,"name":"Background","opacity":1,"active":true,"unqid":"ynvcrq","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":1,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAgxJREFUWEfNmNGOQyEIROv/f/RtrhEyjoOgfek+bVLF4wAjbfv80V/7heV5nkftb61dxb3ahBB8LvKdQh3BRBAGgGA3UCWYDIJTdQu1hckgVGmQIs5ZUUrCHEJgDC/oG6gFxkA2UvfbBo3UP8v24n4s8gmGQbLbjUBTe7974ID+GYMbLBR+51hgeCHdtAHwu1f6jMV91yqV8BIIz2lack63VzAIZf/juiWtDGhKKhgPrtJE0kpl2HeiOC9UpowB7jqj3xxU8z0VECiPnvWtMllrn75nu7pJYawws27iNubO2bX4gCilaWlXUyM6ANWq+ky1Zl6YSu1MnULPg3TmEddqpaxMD6bqJ0ofqLNchDztHAYNTpld1DnWZeTGmMk7GLZ8OMCt3nKPBc3rBolZgqWnniayc3ZazyIUYlRDk5Hi+mpr+y1I325SBkot6jD8WFoMXv8TDD+Cu+ADQL3qx2nyDZZveKVDZdQIYe1spniUJkyBcFlveRF0KkhWBou67MBYuDwAWdXi3BPdlHtZzDFpN/lkBo7q8wk/C6pmhpqVWSmGUYqwd6gZd9PakxNv5ho9dqrD1CMZtaoo9mkG3owT8wwcvUU8GginValNITid4Ze4kwELnwMs2kTl5ez0620FSsHcfMVNYaA23EnFdM+CTG9U0F3LnjJMBgWRJxs4+VnkGEZBKVlOIGz/F/AshkJzdMK+AAAAAElFTkSuQmCC","edit":false,"name":"Layer 1","opacity":1,"active":false,"unqid":"eiymk","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":2,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAi5JREFUWEfdWMF2wzAIS/7/o9NnD5iQBU7THva2YxZjIYQgPY8/9Hd+guW6rkudP8/zUdxHhz5JoDvbgvHMHyYq73UyFXslGAVkBPIgVYnazK16GAffb8EAI+M918c5gLzLFjByVOxIMMgKatQBYODjOCKGYsuYnIkYI5MMxU4Jhi92OvE5MeevpA6DSwPrOKfYWcAoVvDwuJGCzUeGRLa6YCTYsXjzvARTlAOzQaqDNNNV0hc8s+r84FXsJDAVK6h4FiJoAkFwU+E98xoq98oMdgkf8GxYiMauX+CAsGzMfqmdpRMqPTSsxcWdoXmNVAO4yBOYXadw64puwqwrCUz2VLkXMIU5hQZ4NhYJhNiTw8L8VF6VhFUYE3dLmFenq24sKCOdDQaHEnXjn4Xtx3PKTrKxm/BY6q+C2V28+X9mhsrEzISPeHlUmZ4AMnZ+wXhJCkNCm58gcVZxue4AEmdWMMiO8g0GokywsYClK93XhpOXptdM7WCmGZiJHF7UlPATmJ1DclmEv6Quo5kVLscJIPt3B6Wy/DTFUWtCMwub20HZseMXqN1EuGmsqL5CYIPcWiFw4RB6WJjAoLheMjO87Yl9SS9Xih27NPlOMQqWnaaadTQSejBMZeMrM1D36dKtDVa+GkzDzsREYpVLvYEPcywYcrb3YJwd0sBb301qQoOGUuguq+QNdqp9fzMG4tvJNcjvb4N/83t7t5ZuwdwZet9653+A2f0K8eQHoxeuA2tCWhaNlwAAAABJRU5ErkJggg==","edit":false,"name":"Layer 2","opacity":1,"active":false,"unqid":"eiymk","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":3,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAh9JREFUWEfNmEuSwyAQQ8f3P7SnIIgSQg0NmcVkk8SB9EP9tZ+ff/R6vmF53/d1+5/nufrfo01svBmcYJjvFCoFIxBWzALBgtxALWEiCPVOgRDjHfgEysJkIWBRYfg6PmegBpgMhHMFYPCbUy4DNcEs/M6xUvbV4EWsaMwExif3cZCrm7oBI3VdC/VYBVUGkOXdqaXXADTBQGIpFdadDMHKGDcNSirkEoZAnqKEqxftunVTA+sHCNSsgLzWKtNgAKKxUr9HMALSiyIrTmtyMBKQXeYSBkkYgEzB3k6XVybIjkF6FzORMpR5gPsbGLjIpTZgeA2nOfW0a5hdNvRAhhtRh9p3rknXynCs1IxUVZCmGmNGGc7MApdXhk7IVbOkeYdyRU3LQltTQfC5qXYNw3MLgng3y6AE9MNQvbpSxkHUuNXec9DTsP9IGXeyqdFxRWS3cX/S61d1xjTLcIhaGQ8Gr70yyJjVKVWN1ffNKLpuB1on3Kmks3NAD+kbjaJsI9sorUuCEWNopq5LG5en3BQGLoEMVZQDUtw8NFiFlH716cD06p12t5HTW/7DFbidyp9KOpB8BqlJGQnmYYRYgNgyEIyhHiZbK4I72wqwmnu5BJSKzJOkuqkP3FkoUfekMk+2pwvQ9vQeivYN6phiGNoMfziBGvKaHkBk7iKHvdlqulPq25v+KZsyYKsnEqdKqL2tmyLA6EER1p8+myn7fgE8c4RCu8Kc0wAAAABJRU5ErkJggg==","edit":false,"name":"Layer 3","opacity":1,"active":false,"unqid":"eiymk","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":4,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAiZJREFUWEfNWFEWwiAMc/c/9Hwg4YWQdgx+9EcdUEJI2ur1+aPXdYLlvu/brb+uayvu1qKTA2RrUzA4+eZBh31BYsZaCMYAOWGxXucToBQMGGFprLJU1jQWBl1lgCwYZoUWM+0ZS50FPQx/d9cVgpGFFUhgnjpGwbvJSgxdw88U0AQmYiVgqOJQfbUr6gcomwZz6nOAsmAMK1gUObOPM2g9gBtjVgcwGSskxrIGoqyfdRN3xTiFspaC0UCifptxsUHCCg5t9TRd04JWekDoBGIEa0Yrg4AjU1gwmRXFMaqd8MRavzLmu2aaqjlLTmI1Nu36cVrQAyj7mndYwJqyl+vhg0tYZ4MrWWvlqgYwWVJ7Qubop/DKYHchz5mYedo0GzcW5zQwJEFNCTWJU/Dhmt6AcsLPCiW7r+nq95aBWanQLsuS5RE+FHp0Ta0W/vTGQJyWdNy1G1EcKZbVwYOAA2tzgQtLR8HeKLBXrUZBBxBaG2CiiU5DUomXUr6rZchHQ9IDraIDrezl9LY0tOerzZW6qwfFQOkteFLFpyI3LEGgtpXg+UExrXsstRDKhLYQbxhhUWs/vNpcQfFR+kn14q5fmvyZGfa22o/bBkFkc9WT1WHntO0EoKBb61ZnQFGNi37q8PUsgQmctVwlTDlwxXKIN2kGo5p3llG0iWCL4yCHIa9ozBSMpvO3gHR+9mtysvbpZqfrQ2ZOA++s3wYT/VEEEDt/GH0BxdBvQrBEJaEAAAAASUVORK5CYII=","edit":false,"name":"Layer 4","opacity":1,"active":true,"unqid":"eiymk","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}}],"active":true,"selectedLayer":4,"unqid":"m1fr0o","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAY1JREFUWEftWNEOwkAI8/7/o2dmhmFcW3rTGB/mk0bkSoF259i2bXv8yWvcYEgnLGbGGB830pmGFkwAcZIxxG4OCQYlcVkK8DVeFdWCQUllwqOle0wtpmOIgsk/7JLk9uyxuYAKPH9f2yrBoKSKdqcAVRgEU5Oy/kdlDHT9nWJsz0XBqAMU2DoriEnGzgSGHfRCTvQmADgMyXmqdrAygCjWKQZt2tSm1VWMmekKqNu2f65sQjBqWJG+OBs0rfCx/lPx0SbGimNKna6grUPsvAdYiZEDCM2KYi3OO8VUZpyDWcyKSMLhZ21aAYVa3BnlMhimKxkoUlnYAmKgsE0haq5LZ6BMN6pQtqKYRW9FL5jGxJY4D1p0tXMVqlJ0CCsCbagqGHpTpxsrJpqZ6gTyJ0bJBn568EL3po72KwLHGDr5FgOT5bq+r3PTbQnaVOhz7Eap2EG0uwvAHqwm11ZW362su+rKAy9dVRyrYMqsimrBuIwogFWtqSB2/0I4/uQw5RTV3rXdg74Rd4NhLD4BqUiGtstfWkUAAAAASUVORK5CYII=","width":"35","height":"35"}],"currentFrame":0,"name":"Untitled","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMA/sfR5H8Fkddasdmnacvx//8745jkhasdASD945kjknhj/AAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAY1JREFUWEftWNEOwkAI8/7/o2dmhmFcW3rTGB/mk0bkSoF259i2bXv8yWvcYEgnLGbGGB830pmGFkwAcZIxxG4OCQYlcVkK8DVeFdWCQUllwqOle0wtpmOIgsk/7JLk9uyxuYAKPH9f2yrBoKSKdqcAVRgEU5Oy/kdlDHT9nWJsz0XBqAMU2DoriEnGzgSGHfRCTvQmADgMyXmqdrAygCjWKQZt2tSm1VWMmekKqNu2f65sQjBqWJG+OBs0rfCx/lPx0SbGimNKna6grUPsvAdYiZEDCM2KYi3OO8VUZpyDWcyKSMLhZ21aAYVa3BnlMhimKxkoUlnYAmKgsE0haq5LZ6BMN6pQtqKYRW9FL5jGxJY4D1p0tXMVqlJ0CCsCbagqGHpTpxsrJpqZ6gTyJ0bJBn568EL3po72KwLHGDr5FgOT5bq+r3PTbQnaVOhz7Eap2EG0uwvAHqwm11ZW362su+rKAy9dVRyrYMqsimrBuIwogFWtqSB2/0I4/uQw5RTV3rXdg74Rd4NhLD4BqUiGtstfWkUAAAAASUVORK5CYII=","palette_id":false} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d8.pixil b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d8.pixil new file mode 100644 index 000000000..4f7ff440e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/d8.pixil @@ -0,0 +1 @@ +{"application":"pixil","version":"2.6.1","website":"pixilart.com","author":"https://www.pixilart.com","contact":"support@pixilart.com","width":"35","height":"35","colors":{"default":["000000","ffffff","f44336","E91E63","9C27B0","673AB7","3F51B5","2196F3","03A9F4","00BCD4","009688","4CAF50","8BC34A","CDDC39","FFEB3B","FFC107","FF9800","FF5722","795548","9E9E9E","607D8B","ffebee","ffcdd2","ef9a9a","e57373","ef5350","e53935","d32f2f","c62828","b71c1c","ff8a80","ff5252","ff1744","d50000","fce4ec","f8bbd0","f48fb1","f06292","ec407a","e91e63","d81b60","c2185b","ad1457","880e4f","ff80ab","ff4081","f50057","c51162","f3e5f5","e1bee7","ce93d8","ba68c8","ab47bc","9c27b0","8e24aa","7b1fa2","6a1b9a","4a148c","ea80fc","e040fb","d500f9","aa00ff","ede7f6","d1c4e9","b39ddb","9575cd","7e57c2","673ab7","5e35b1","512da8","4527a0","311b92","b388ff","7c4dff","651fff","6200ea","e8eaf6","c5cae9","9fa8da","7986cb","5c6bc0","3f51b5","3949ab","303f9f","283593","1a237e","8c9eff","536dfe","3d5afe","304ffe","e3f2fd","bbdefb","90caf9","64b5f6","42a5f5","2196f3","1e88e5","1976d2","1565c0","0d47a1","82b1ff","448aff","2979ff","2962ff","e1f5fe","b3e5fc","81d4fa","4fc3f7","29b6f6","03a9f4","039be5","0288d1","0277bd","01579b","80d8ff","40c4ff","00b0ff","0091ea","e0f7fa","b2ebf2","80deea","4dd0e1","26c6da","00bcd4","00acc1","0097a7","00838f","006064","84ffff","18ffff","00e5ff","00b8d4","e0f2f1","b2dfdb","80cbc4","4db6ac","26a69a","009688","00897b","00796b","00695c","004d40","a7ffeb","64ffda","1de9b6","00bfa5","e8f5e9","c8e6c9","a5d6a7","81c784","66bb6a","4caf50","43a047","388e3c","2e7d32","1b5e20","b9f6ca","69f0ae","00e676","00c853","f1f8e9","dcedc8","c5e1a5","aed581","9ccc65","8bc34a","7cb342","689f38","558b2f","33691e","ccff90","b2ff59","76ff03","64dd17","f9fbe7","f0f4c3","e6ee9c","dce775","d4e157","cddc39","c0ca33","afb42b","9e9d24","827717","f4ff81","eeff41","c6ff00","aeea00","fffde7","fff9c4","fff59d","fff176","ffee58","ffeb3b","fdd835","fbc02d","f9a825","f57f17","ffff8d","ffff00","ffea00","ffd600","fff8e1","ffecb3","ffe082","ffd54f","ffca28","ffc107","ffb300","ffa000","ff8f00","ff6f00","ffe57f","ffd740","ffc400","ffab00","fff3e0","ffe0b2","ffcc80","ffb74d","ffa726","ff9800","fb8c00","f57c00","ef6c00","e65100","ffd180","ffab40","ff9100","ff6d00","fbe9e7","ffccbc","ffab91","ff8a65","ff7043","ff5722","f4511e","e64a19","d84315","bf360c","ff9e80","ff6e40","ff3d00","dd2c00","efebe9","d7ccc8","bcaaa4","a1887f","8d6e63","795548","6d4c41","5d4037","4e342e","3e2723","fafafa","f5f5f5","eeeeee","e0e0e0","bdbdbd","9e9e9e","757575","616161","424242","212121","eceff1","cfd8dc","b0bec5","90a4ae","78909c","607d8b","546e7a","455a64","37474f","263238"],"simple":["ffffff","d4d4d4","a1a1a1","787878","545454","303030","000000","edc5c5","e68383","ff0000","de2424","ad3636","823737","592b2b","f5d2ee","eb8dd7","f700b9","bf1f97","9c277f","732761","4f2445","e2bcf7","bf79e8","9d00ff","8330ba","6d3096","502c69","351b47","c5c3f0","736feb","0905f7","2e2eb0","2d2d80","252554","090936","c7e2ed","6ac3e6","00bbff","279ac4","347c96","2d5b6b","103947","bbf0d9","6febb3","00ff88","2eb878","349166","2b694c","0c3d25","c2edc0","76ed70","0dff00","36c72c","408c3b","315c2e","144511","d6edbb","b5eb73","8cff00","89c93a","6f8f44","4b632a","2a400c","f1f2bf","eef069","ffff00","baba30","91913f","5e5e2b","3b3b09","ffdeb8","f2ae61","ff8400","c48037","85623d","573e25","3d2309","fcbbae","ff8066","ff2b00","cc553d","9c5b4e","61372e","36130b"],"common":["000000","FFFFFF","7F7F7F","a1a1a1","C3C3C3","c40424","880015","B97A57","dba88c","ED1C24","f75b63","f26f9b","FF7F27","f7ab79","FFC90E","FFF200","cfc532","EFE4B0","1ee656","0c6624","22B14C","B5E61D","5487ff","00A2E8","99D9EA","3F48CC","7f86e3","7092BE","720899","cd55cf","A349A4","C8BFE7","ffffff"],"skin tones":["ffe0bd","ffdbac","ffcd94","eac086","e0ac69","f1c27d","ffad60","c68642","8d5524","896347","765339","613D24","4C2D17","391E0B","351606","2D1304","180A01","090300"]},"frames":[{"name":"","speed":100,"layers":[{"id":0,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAFtJREFUWEftlEEOACAIw9z/Hz0foBwWTdCknomQUibbHo88MUyxCchUikIGMml84QzO4ExKAGdSYkvOSGrLnrbGO2oMwzUdX1P6wc16BEbg1CecwRmcSQl84cwEPw2PmDu06wcAAAAASUVORK5CYII=","edit":false,"name":"Background","opacity":"1","active":true,"unqid":"f9mfzy5","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":1,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAhRJREFUWEfNmFtu7DAMQ5v9LzqFDUugGephX1yg/ZlM09jHFPVIn59//Hnf9+Ulnud5bpa9esg2MhDc29hugK5hFAhAzstToCuYQpEJMhT67zCRImidEbabcB0pI0DG8x8Pm4dOgdowDAJhcBhSxMPV9U8Jg8cWJ0Z/eDaDIkdAKUzgDw8NK7HUYoC2f0KYBGQmy8hc84tS7EadFIY3sTislJ2V19TgT7u3/NIKl4QZqmT+sA3o9LyWwzJQVH8+MAgyQgHfZ1iUGqgC9qQifJ+9t18QyDzQWtyaoQMxFJ4+ClFVfxyGFUGTmlkjAMwiVCaqRQTrDBIGFkdlMGRu3LW5KaY+LfswxWX/CmFAdgylzy4ILMK7bRYlBLeL0DNcJxKPzDVsQ0xxaANbXYqyK80mBOJrGuY2haEeoYX8Wqw1n5cwqlCJ09jzn7GTTayIMLOs7pRFj9NUhc/qkZTBHB6MxTiEhTBk4NmqsDHebqxUT5VBQ1LqenpWvmALQOPdBjJsDe2uHRU2gN1GvuBthVvKtn8IY+qYrCpMa0MvjFwsaQzZQCDtPeIpjAgXFjPMRpVRYZNVIDK12ZjRa0mhSjV8SRFKZThcBkshqZRpjZ4tGAWUVeSbkbMVJqqmUwFRPfF1ZZuBTt6d2spAeDagbAA7ATlWpgE0I0otpH3g9h92sgzNHaVv2kaym9W9P/P/mQr05v4vDlEtQnsbdYsAAAAASUVORK5CYII=","edit":false,"name":"Layer 1","opacity":"1","active":false,"unqid":"rly08e","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":2,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAchJREFUWEfNmNtygzAQQ8P/fzQdezAjhGSv3DwkL02L8R6kvZgenx/6HDss53me7b7jeN9+XWrX4r3jGxpEgxmxRnCE2wWKYYwqbZ+u1vjsAG3BKFUGBLqTAkUwqAoEUmpsKRTDoCqr76llZRinCiewUuxK+s42q7IIxinB+dKAZmsdUAlmpUoLjIq47yuFyjDqSZvqomkO9oc6lT60hJlU0HFHBSIu+6EaN+TLykf8EswqV1gdo2K3kq7VYVJVEIrzCMsc1MpgXK6osbAq821lZqrgCGKLuM+o32NleDJDAvbEvXpFG45jSPafNI9kZZVhsEKE793j4DzTJzn3nTKMOq+oCmAbCRwT86XOAORO/CrtVQVdYA+FqIrGnn0r1XcimEpfmQ08tpqtimHc9BXtv/QnVfYlm9D/UqRwUTmB3WE7jGeXR+NgVrbfAnJnYzkoK60ek9JBugnuTnx2as+AOKdUN1Z9yFXRfVKcSa9KFKcvHTfdWLhDrF5dlucZbP94WlO9Ax8MGx6ttTFLMKDG6x3b9KPHG+ZKkZJNykJnnXrnriqyDcMqsXVslasc9aCRTTtKJf8a+TeMUgqqLNo/WvytDuz2+QMl8/wzbBarJwAAAABJRU5ErkJggg==","edit":false,"name":"Layer 2","opacity":"1","active":true,"unqid":"tqny2","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":3,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAgdJREFUWEfNmEF2AyEMQ8v9Dz15UOwnhAyGySLdpZ2BH1kWpuXv8ud5nodfLaWUy+Xaa1cvGwjubWxvgI5hFIip8RboCsYUgc29Om+AjmBQFbQMW+UWKA3D5WH/dqC6XjP2DVAKJgKpAFSqUp8VZUzts31oBYLGjXxE5VzudwTDhq2fWR37XY8Nz6JM2ZYwB+Vp5SLf2NppoBBmVx5UxdSBrmrewTTGMkbBuIRZ5Am/53sLU7PJTcVpbwmj8oTBFt8aS2ZHTluyK+KtzwpNMKo8ycRlCAfp4NJDCDTAbHwyZAgqs+kqhLJQHMLRgCaYhE+qzLwoL95YqTSuDHTe4B+H2fmk0mO6CmUaEG5E5el8/02mumuACVRxCAw9au3hW9tmDBaEpjNMMGosYBXsM3XDEG4qmSNFfD04X6YDToyVu6lyKpUaNaKjQZaJdxTj7vAI5geND1PgkbHHdVAZlhE/nwIK37hq/W/rBKac8fNFnCXTzQAVAU+hHcLknTyjvMNjADzjQgFoCJgdtrbHgQEJ02GmTK3NZbqeZ8R1BLvE05Z8JBMW1dzdqY7mGTE8hercXPCOJz1uY1BHHgeZ8oQGFi3cjLkbIzZD1fJLp2F6yw5AdkdXUZDtHBXlKeIFkLXzMDidlAah0jArhfqC1zfJozIh/U/9FyJQ6Opuzb45KpMy3Td/9wFxxy9CEK7yBQAAAABJRU5ErkJggg==","edit":false,"name":"Layer 3","opacity":"1","active":false,"unqid":"rly08e","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}},{"id":4,"src":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAcZJREFUWEfNWFsOwzAIW+5/6E6JAmLU5pF10vYzqXk5xmDa8fqj3zjBcl3XNdeNcV++h+ZYe+/2Ag9EDrfg5rOfgwkYmZdabM3fKTtlZhAQy4oAkeicsFMCEwGx0jCMHLGTgmGh2TfXsDhG7PP0DGU1yqYIiAjWC/gbdihqlDXs9kQvbXYgGJa+Ewy5eeV5Gq7bhAiIS9sVKRDmleJAU2dgfPGUjS0rFpgFtIud3GlV6WrdgcwgbaDbMvGfZhYFY2n24UHF7gl2yswg4XqzdOFoa6fEDNMKMWa7Z0s7J2CGyzgxSf2f4yLkTmalYDwrM52b/UyZnVAzUV2xgEDqr303Q8y/bmenAgZNkxY7vbJJJdNUreFO3WmDMT6U+lrXs0LN+M0ih6+M+fLgW9MSmMpBnTliEWUwzHs6h6K5UVuauva3h6P1zDjTfqbi1M6XNJWR5iIHDzPCuy/wno+qaw8HxTJtJdL0ZN3/bqymGS5A6GVOwLkxembYfbFXlOS1VqNWBaGMZgIlPiTh+dAHYTFtN8tgxGNY75I8LwNRj8nYYYDsum5IYA2qADFCvH0KeQJEK0yOAf3aYFK5FQ5GwCObdNiN5r4BawL8M515sSoAAAAASUVORK5CYII=","edit":false,"name":"Layer 4","opacity":"1","active":false,"unqid":"tqny2","options":{"blend":"source-over","locked":false,"filter":{"brightness":"100%","contrast":"100%","grayscale":"0%","blur":0,"dropshadow_x":0,"dropshadow_y":0,"dropshadow_blur":0,"dropshadow_alpha":1}}}],"active":true,"selectedLayer":2,"unqid":"jv591w","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAYNJREFUWEfNmEsSwjAMQ8n9D12mzIQxqmXJbhew4VvyoihSYB3Hcbz+5LbuwKy1LtO4M7cxzAaJg2evdUS/DXMOdgKhShOFRjCVAhGqCzSG2QNlg0+B2jBRFfV4+8VVaAQTVUEDZ4ptXykzt2CYEnsQpZRSqA2TqXJCOK8rhWwYpUpUB5eKPcdla8Ewr8QvxW2/VXNyyIJxVam8g+GY+ceGcVRBhVgWRY/9XKOKUqnCAg5N/YgybKecX161duadCNReJkeVXZLxXu2s0TJVqihl4vvotzaMUgVnTweAQKz6iu4mJ1WrrsF8cYIvhXFVUcXHlorVAoVh7esAZJ9h7S5zhq3/FOTSQcFHFoxq2ClYeWRlCZwF2hQAr2PnmrKbqqh3lGMNziYlizIDyjwVt3Jm1mp5vpmlihKPBVloIUhWkpaSLkwGVW1//GGnzr+fTOrCYCdVZ5Yq+jPfjGAqlRissxNvwTA/dRVpG9iaWfiLxPHIJZknnnHAJp95ZJkmA2fXvAFTp/+nAJYHZgAAAABJRU5ErkJggg==","width":"35","height":"35","old_width":"35","old_height":"35"}],"currentFrame":0,"name":"Untitled","preview":"data:image/pngp98kjasdnasd983/24kasdjasdbase64,iVBORw0KGgoAAAANSUhEUgAAACMA/sfR5H8Fkddasdmnacvx//8745jkhasdASD945kjknhj/AAAjCAYAAAAe2bNZAAAAAXNSR0IArs4c6QAAAYNJREFUWEfNmEsSwjAMQ8n9D12mzIQxqmXJbhew4VvyoihSYB3Hcbz+5LbuwKy1LtO4M7cxzAaJg2evdUS/DXMOdgKhShOFRjCVAhGqCzSG2QNlg0+B2jBRFfV4+8VVaAQTVUEDZ4ptXykzt2CYEnsQpZRSqA2TqXJCOK8rhWwYpUpUB5eKPcdla8Ewr8QvxW2/VXNyyIJxVam8g+GY+ceGcVRBhVgWRY/9XKOKUqnCAg5N/YgybKecX161duadCNReJkeVXZLxXu2s0TJVqihl4vvotzaMUgVnTweAQKz6iu4mJ1WrrsF8cYIvhXFVUcXHlorVAoVh7esAZJ9h7S5zhq3/FOTSQcFHFoxq2ClYeWRlCZwF2hQAr2PnmrKbqqh3lGMNziYlizIDyjwVt3Jm1mp5vpmlihKPBVloIUhWkpaSLkwGVW1//GGnzr+fTOrCYCdVZ5Yq+jPfjGAqlRissxNvwTA/dRVpG9iaWfiLxPHIJZknnnHAJp95ZJkmA2fXvAFTp/+nAJYHZgAAAABJRU5ErkJggg==","palette_id":false} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/sources/flipper-screen.png b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/flipper-screen.png new file mode 100644 index 000000000..af759a20f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/flipper-screen.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/sources/main-screen.png b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/main-screen.png new file mode 100644 index 000000000..20a4e9c2c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/main-screen.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/dice/sources/roll-screen.png b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/roll-screen.png new file mode 100644 index 000000000..2dda26e59 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/dice/sources/roll-screen.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/README.md b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/README.md new file mode 100644 index 000000000..8c4e45ec0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/README.md @@ -0,0 +1,13 @@ +# FlipperZero-Etch-A-Sketch +Turn the Flipper Zero into an Etch A Sketch + +This is a modification of the original paint app. + +![](Screenshot.png) + + ## Changes + - LED indicator turns red when draw mode is enabled + - Pressing and holding the DPad will continously draw + - Vibration effects added for mechanical feedback + - Audio cues added for feedback + - Smaller brush size for more creativity \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/Screenshot.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/Screenshot.png new file mode 100644 index 000000000..6cc086ec9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/Screenshot.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/application.fam b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/application.fam new file mode 100644 index 000000000..64b7a0a8a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/application.fam @@ -0,0 +1,12 @@ +App( + appid="etch", + name="Etch A Sketch", + apptype=FlipperAppType.EXTERNAL, + entry_point="etch_a_sketch_app", + cdefines=["APP_ETCH_A_SKETCH"], + requires=["gui"], + stack_size=2 * 1024, + order=175, + fap_icon="etch-a-sketch-icon.png", + fap_category="Misc_Extra", +) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/125_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/125_10px.png new file mode 100644 index 000000000..ce01284a2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/125_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ActiveConnection_50x64.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ActiveConnection_50x64.png new file mode 100644 index 000000000..1d7686ddd Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ActiveConnection_50x64.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Alert_9x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Alert_9x8.png new file mode 100644 index 000000000..d03f107ef Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Alert_9x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowC_1_36x36.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowC_1_36x36.png new file mode 100644 index 000000000..3a0c6dd0c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowC_1_36x36.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowDownEmpty_14x15.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowDownEmpty_14x15.png new file mode 100644 index 000000000..8c6d54f9c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowDownEmpty_14x15.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowDownFilled_14x15.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowDownFilled_14x15.png new file mode 100644 index 000000000..6cef0f4a7 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowDownFilled_14x15.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowUpEmpty_14x15.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowUpEmpty_14x15.png new file mode 100644 index 000000000..261c6d89e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowUpEmpty_14x15.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowUpFilled_14x15.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowUpFilled_14x15.png new file mode 100644 index 000000000..fa35eb2f8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ArrowUpFilled_14x15.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Attention_5x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Attention_5x8.png new file mode 100644 index 000000000..137d4c4d0 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Attention_5x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Auth_62x31.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Auth_62x31.png new file mode 100644 index 000000000..40f094ac9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Auth_62x31.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/BLE_Pairing_128x64.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/BLE_Pairing_128x64.png new file mode 100644 index 000000000..34068c300 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/BLE_Pairing_128x64.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Background_128x11.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Background_128x11.png new file mode 100644 index 000000000..78ef029ae Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Background_128x11.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/BatteryBody_52x28.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/BatteryBody_52x28.png new file mode 100644 index 000000000..1fe568346 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/BatteryBody_52x28.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Battery_16x16.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Battery_16x16.png new file mode 100644 index 000000000..49af3c225 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Battery_16x16.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Battery_26x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Battery_26x8.png new file mode 100644 index 000000000..5fc1b0cd6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Battery_26x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Ble_connected_15x15.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Ble_connected_15x15.png new file mode 100644 index 000000000..64dab9b53 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Ble_connected_15x15.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Ble_disconnected_15x15.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Ble_disconnected_15x15.png new file mode 100644 index 000000000..0858bb93f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Ble_disconnected_15x15.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Bluetooth_Connected_16x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Bluetooth_Connected_16x8.png new file mode 100644 index 000000000..c77bc1494 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Bluetooth_Connected_16x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Bluetooth_Idle_5x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Bluetooth_Idle_5x8.png new file mode 100644 index 000000000..dc4a8733c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Bluetooth_Idle_5x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonCenter_7x7.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonCenter_7x7.png new file mode 100644 index 000000000..a66461b22 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonCenter_7x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonDown_7x4.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonDown_7x4.png new file mode 100644 index 000000000..2954bb6a6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonDown_7x4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonLeftSmall_3x5.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonLeftSmall_3x5.png new file mode 100644 index 000000000..51411acaf Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonLeftSmall_3x5.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonLeft_4x7.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonLeft_4x7.png new file mode 100644 index 000000000..0b4655d43 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonLeft_4x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonRightSmall_3x5.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonRightSmall_3x5.png new file mode 100644 index 000000000..b9d5f87db Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonRightSmall_3x5.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonRight_4x7.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonRight_4x7.png new file mode 100644 index 000000000..8e1c74c1c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonRight_4x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonUp_7x4.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonUp_7x4.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ButtonUp_7x4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Button_18x18.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Button_18x18.png new file mode 100644 index 000000000..30a5b4fab Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Button_18x18.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Certification1_103x56.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Certification1_103x56.png new file mode 100644 index 000000000..6e11bbbb2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Certification1_103x56.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Certification2_98x33.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Certification2_98x33.png new file mode 100644 index 000000000..49c5581c7 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Certification2_98x33.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Charging-lightning_9x10.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Charging-lightning_9x10.png new file mode 100644 index 000000000..c2eaa47d0 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Charging-lightning_9x10.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Charging-lightning_mask_9x10.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Charging-lightning_mask_9x10.png new file mode 100644 index 000000000..d44a32ae0 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Charging-lightning_mask_9x10.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Circles_47x47.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Circles_47x47.png new file mode 100644 index 000000000..6a16ebf7b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Circles_47x47.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Clock_18x18.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Clock_18x18.png new file mode 100644 index 000000000..ab06d008e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Clock_18x18.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Connect_me_62x31.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Connect_me_62x31.png new file mode 100644 index 000000000..68c48c0e6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Connect_me_62x31.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Connected_62x31.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Connected_62x31.png new file mode 100644 index 000000000..eeaf660b1 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Connected_62x31.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/CoolHi_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/CoolHi_25x27.png new file mode 100644 index 000000000..cea29a5b9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/CoolHi_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/CoolHi_hvr_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/CoolHi_hvr_25x27.png new file mode 100644 index 000000000..692ac7b8b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/CoolHi_hvr_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/CoolLo_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/CoolLo_25x27.png new file mode 100644 index 000000000..23288e44f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/CoolLo_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/CoolLo_hvr_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/CoolLo_hvr_25x27.png new file mode 100644 index 000000000..ae5316e4d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/CoolLo_hvr_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Cry_dolph_55x52.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Cry_dolph_55x52.png new file mode 100644 index 000000000..86d9db1b4 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Cry_dolph_55x52.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DFU_128x50.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DFU_128x50.png new file mode 100644 index 000000000..951cdc198 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DFU_128x50.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Dehumidify_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Dehumidify_25x27.png new file mode 100644 index 000000000..dca77ae41 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Dehumidify_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Dehumidify_hvr_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Dehumidify_hvr_25x27.png new file mode 100644 index 000000000..2c593ca8d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Dehumidify_hvr_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Detailed_chip_17x13.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Detailed_chip_17x13.png new file mode 100644 index 000000000..9aaa1c555 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Detailed_chip_17x13.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinCommon_56x48.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinCommon_56x48.png new file mode 100644 index 000000000..089aaed83 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinCommon_56x48.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinMafia_115x62.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinMafia_115x62.png new file mode 100644 index 000000000..66fdb40ff Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinMafia_115x62.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinNice_96x59.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinNice_96x59.png new file mode 100644 index 000000000..a299d3630 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinNice_96x59.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinReadingSuccess_59x63.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinReadingSuccess_59x63.png new file mode 100644 index 000000000..46f559f65 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinReadingSuccess_59x63.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinWait_61x59.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinWait_61x59.png new file mode 100644 index 000000000..423e07919 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DolphinWait_61x59.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DoorLeft_70x55.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DoorLeft_70x55.png new file mode 100644 index 000000000..5df87ba3c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DoorLeft_70x55.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DoorRight_70x55.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DoorRight_70x55.png new file mode 100644 index 000000000..0cc1e65e6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/DoorRight_70x55.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Down_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Down_25x27.png new file mode 100644 index 000000000..c13097778 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Down_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Down_hvr_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Down_hvr_25x27.png new file mode 100644 index 000000000..76d181924 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Down_hvr_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Drive_112x35.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Drive_112x35.png new file mode 100644 index 000000000..6f7b9c834 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Drive_112x35.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Error_18x18.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Error_18x18.png new file mode 100644 index 000000000..16a5a74d9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Error_18x18.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Error_62x31.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Error_62x31.png new file mode 100644 index 000000000..bb280e751 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Error_62x31.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/EviSmile1_18x21.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/EviSmile1_18x21.png new file mode 100644 index 000000000..987af3258 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/EviSmile1_18x21.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/EviSmile2_18x21.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/EviSmile2_18x21.png new file mode 100644 index 000000000..7e28c9f01 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/EviSmile2_18x21.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/EviWaiting1_18x21.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/EviWaiting1_18x21.png new file mode 100644 index 000000000..d39d21733 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/EviWaiting1_18x21.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/EviWaiting2_18x21.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/EviWaiting2_18x21.png new file mode 100644 index 000000000..15ca088fd Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/EviWaiting2_18x21.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/FaceCharging_29x14.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/FaceCharging_29x14.png new file mode 100644 index 000000000..106ededbf Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/FaceCharging_29x14.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/FaceConfused_29x14.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/FaceConfused_29x14.png new file mode 100644 index 000000000..dcd2e3c67 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/FaceConfused_29x14.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/FaceNopower_29x14.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/FaceNopower_29x14.png new file mode 100644 index 000000000..f3da0c8ca Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/FaceNopower_29x14.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/FaceNormal_29x14.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/FaceNormal_29x14.png new file mode 100644 index 000000000..52d78c086 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/FaceNormal_29x14.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/GameMode_11x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/GameMode_11x8.png new file mode 100644 index 000000000..49f2e25bf Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/GameMode_11x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Health_16x16.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Health_16x16.png new file mode 100644 index 000000000..af343c520 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Health_16x16.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/HeatHi_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/HeatHi_25x27.png new file mode 100644 index 000000000..a1724f995 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/HeatHi_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/HeatHi_hvr_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/HeatHi_hvr_25x27.png new file mode 100644 index 000000000..b92108d68 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/HeatHi_hvr_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/HeatLo_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/HeatLo_25x27.png new file mode 100644 index 000000000..af2e59d49 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/HeatLo_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/HeatLo_hvr_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/HeatLo_hvr_25x27.png new file mode 100644 index 000000000..6708edb36 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/HeatLo_hvr_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Hidden_window_9x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Hidden_window_9x8.png new file mode 100644 index 000000000..d6fc2b326 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Hidden_window_9x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/InfraredArrowDown_4x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/InfraredArrowDown_4x8.png new file mode 100644 index 000000000..2ac7bcdbe Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/InfraredArrowDown_4x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/InfraredArrowUp_4x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/InfraredArrowUp_4x8.png new file mode 100644 index 000000000..4c9a16b3f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/InfraredArrowUp_4x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/InfraredLearnShort_128x31.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/InfraredLearnShort_128x31.png new file mode 100644 index 000000000..783ad0877 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/InfraredLearnShort_128x31.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/KeyBackspaceSelected_16x9.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/KeyBackspaceSelected_16x9.png new file mode 100644 index 000000000..7cc0759a8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/KeyBackspaceSelected_16x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/KeyBackspace_16x9.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/KeyBackspace_16x9.png new file mode 100644 index 000000000..9946232d9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/KeyBackspace_16x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/KeySaveSelected_24x11.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/KeySaveSelected_24x11.png new file mode 100644 index 000000000..eeb3569d3 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/KeySaveSelected_24x11.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/KeySave_24x11.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/KeySave_24x11.png new file mode 100644 index 000000000..e7dba987a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/KeySave_24x11.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Keychain_39x36.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Keychain_39x36.png new file mode 100644 index 000000000..d15850b5b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Keychain_39x36.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Left_mouse_icon_9x9.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Left_mouse_icon_9x9.png new file mode 100644 index 000000000..c533d8572 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Left_mouse_icon_9x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Lock_7x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Lock_7x8.png new file mode 100644 index 000000000..f7c9ca2c7 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Lock_7x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Lock_8x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Lock_8x8.png new file mode 100644 index 000000000..01fb0eb6b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Lock_8x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/MHz_25x11.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/MHz_25x11.png new file mode 100644 index 000000000..b99554956 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/MHz_25x11.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Medium-chip-22x21.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Medium-chip-22x21.png new file mode 100644 index 000000000..b1f15432d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Medium-chip-22x21.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Modern_reader_18x34.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Modern_reader_18x34.png new file mode 100644 index 000000000..b19c0f30c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Modern_reader_18x34.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Move_flipper_26x39.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Move_flipper_26x39.png new file mode 100644 index 000000000..ff4af9ff0 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Move_flipper_26x39.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Mute_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Mute_25x27.png new file mode 100644 index 000000000..d8812dd4f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Mute_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Mute_hvr_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Mute_hvr_25x27.png new file mode 100644 index 000000000..155bd9004 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Mute_hvr_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/NFC_manual_60x50.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/NFC_manual_60x50.png new file mode 100644 index 000000000..787c0bcfe Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/NFC_manual_60x50.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Nfc_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Nfc_10px.png new file mode 100644 index 000000000..6bc027111 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Nfc_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Off_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Off_25x27.png new file mode 100644 index 000000000..c15100606 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Off_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Off_hvr_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Off_hvr_25x27.png new file mode 100644 index 000000000..d5e5e6f45 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Off_hvr_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Ok_btn_9x9.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Ok_btn_9x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Ok_btn_pressed_13x13.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Ok_btn_pressed_13x13.png new file mode 100644 index 000000000..6b46ba3a8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Ok_btn_pressed_13x13.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Percent_10x14.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Percent_10x14.png new file mode 100644 index 000000000..677911fd4 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Percent_10x14.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_arrow_down_7x9.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_arrow_down_7x9.png new file mode 100644 index 000000000..9687397af Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_arrow_down_7x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_arrow_left_9x7.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_arrow_left_9x7.png new file mode 100644 index 000000000..fb4ded78f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_arrow_left_9x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_arrow_right_9x7.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_arrow_right_9x7.png new file mode 100644 index 000000000..97648d176 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_arrow_right_9x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_arrow_up_7x9.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_arrow_up_7x9.png new file mode 100644 index 000000000..a91a6fd5e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_arrow_up_7x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_attention_dpad_29x29.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_attention_dpad_29x29.png new file mode 100644 index 000000000..984db9cc7 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_attention_dpad_29x29.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_back_arrow_10x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_back_arrow_10x8.png new file mode 100644 index 000000000..3bafabd14 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_back_arrow_10x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_back_full_40x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_back_full_40x8.png new file mode 100644 index 000000000..cd1301512 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_back_full_40x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_pointer_5x3.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_pointer_5x3.png new file mode 100644 index 000000000..edf3d41bb Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_pointer_5x3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_star_7x7.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_star_7x7.png new file mode 100644 index 000000000..42fdea86e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pin_star_7x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Power_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Power_25x27.png new file mode 100644 index 000000000..5ae493fbe Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Power_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Power_hvr_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Power_hvr_25x27.png new file mode 100644 index 000000000..9425072c0 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Power_hvr_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pressed_Button_13x13.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pressed_Button_13x13.png new file mode 100644 index 000000000..823926b84 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Pressed_Button_13x13.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Quest_7x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Quest_7x8.png new file mode 100644 index 000000000..6825247fb Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Quest_7x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/RFIDBigChip_37x36.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/RFIDBigChip_37x36.png new file mode 100644 index 000000000..a60d51d58 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/RFIDBigChip_37x36.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/RFIDDolphinReceive_97x61.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/RFIDDolphinReceive_97x61.png new file mode 100644 index 000000000..e1f5f9f80 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/RFIDDolphinReceive_97x61.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/RFIDDolphinSend_97x61.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/RFIDDolphinSend_97x61.png new file mode 100644 index 000000000..380a970d9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/RFIDDolphinSend_97x61.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/RFIDDolphinSuccess_108x57.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/RFIDDolphinSuccess_108x57.png new file mode 100644 index 000000000..341999109 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/RFIDDolphinSuccess_108x57.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Reader_detect_43x40.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Reader_detect_43x40.png new file mode 100644 index 000000000..d833a5277 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Reader_detect_43x40.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Release_arrow_18x15.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Release_arrow_18x15.png new file mode 100644 index 000000000..187a90345 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Release_arrow_18x15.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Restoring_38x32.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Restoring_38x32.png new file mode 100644 index 000000000..9e058869f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Restoring_38x32.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Right_mouse_icon_9x9.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Right_mouse_icon_9x9.png new file mode 100644 index 000000000..446d7176c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Right_mouse_icon_9x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SDQuestion_35x43.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SDQuestion_35x43.png new file mode 100644 index 000000000..9b9c9a58e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SDQuestion_35x43.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SDcardFail_11x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SDcardFail_11x8.png new file mode 100644 index 000000000..876cfa229 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SDcardFail_11x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SDcardMounted_11x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SDcardMounted_11x8.png new file mode 100644 index 000000000..68bc61921 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SDcardMounted_11x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Scanning_123x52.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Scanning_123x52.png new file mode 100644 index 000000000..ec785948d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Scanning_123x52.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SmallArrowDown_3x5.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SmallArrowDown_3x5.png new file mode 100644 index 000000000..1912e5d24 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SmallArrowDown_3x5.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SmallArrowDown_4x7.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SmallArrowDown_4x7.png new file mode 100644 index 000000000..5c5252b16 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SmallArrowDown_4x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SmallArrowUp_3x5.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SmallArrowUp_3x5.png new file mode 100644 index 000000000..9c6242078 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SmallArrowUp_3x5.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SmallArrowUp_4x7.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SmallArrowUp_4x7.png new file mode 100644 index 000000000..886369abc Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/SmallArrowUp_4x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Smile_18x18.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Smile_18x18.png new file mode 100644 index 000000000..d2aae0dc3 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Smile_18x18.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Space_65x18.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Space_65x18.png new file mode 100644 index 000000000..b60ae5097 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Space_65x18.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Tap_reader_36x38.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Tap_reader_36x38.png new file mode 100644 index 000000000..4e0ba8f05 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Tap_reader_36x38.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Temperature_16x16.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Temperature_16x16.png new file mode 100644 index 000000000..aade43882 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Temperature_16x16.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Unlock_7x8.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Unlock_7x8.png new file mode 100644 index 000000000..9d82b4daf Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Unlock_7x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Unplug_bg_bottom_128x10.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Unplug_bg_bottom_128x10.png new file mode 100644 index 000000000..35d73ba76 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Unplug_bg_bottom_128x10.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Unplug_bg_top_128x14.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Unplug_bg_top_128x14.png new file mode 100644 index 000000000..bafa2c494 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Unplug_bg_top_128x14.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Up_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Up_25x27.png new file mode 100644 index 000000000..b81a02e8a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Up_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Up_hvr_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Up_hvr_25x27.png new file mode 100644 index 000000000..cf71e5965 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Up_hvr_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Updating_32x40.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Updating_32x40.png new file mode 100644 index 000000000..d8f7654b8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Updating_32x40.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/UsbTree_48x22.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/UsbTree_48x22.png new file mode 100644 index 000000000..cc41b5b9a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/UsbTree_48x22.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Vol_down_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Vol_down_25x27.png new file mode 100644 index 000000000..d7ae44558 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Vol_down_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Vol_down_hvr_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Vol_down_hvr_25x27.png new file mode 100644 index 000000000..c556a037a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Vol_down_hvr_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Vol_up_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Vol_up_25x27.png new file mode 100644 index 000000000..c4d9e87a0 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Vol_up_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Vol_up_hvr_25x27.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Vol_up_hvr_25x27.png new file mode 100644 index 000000000..90c2df47d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Vol_up_hvr_25x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Voldwn_6x6.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Voldwn_6x6.png new file mode 100644 index 000000000..d7a82a2df Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Voldwn_6x6.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Voltage_16x16.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Voltage_16x16.png new file mode 100644 index 000000000..94e796872 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Voltage_16x16.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Volup_8x6.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Volup_8x6.png new file mode 100644 index 000000000..4b7ec66d6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Volup_8x6.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/WarningDolphin_45x42.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/WarningDolphin_45x42.png new file mode 100644 index 000000000..d766ffbb4 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/WarningDolphin_45x42.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Warning_30x23.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Warning_30x23.png new file mode 100644 index 000000000..5f7e02dd8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/Warning_30x23.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/_FaceNormal_29x14.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/_FaceNormal_29x14.png new file mode 100644 index 000000000..52d78c086 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/_FaceNormal_29x14.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/back_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/back_10px.png new file mode 100644 index 000000000..f9c615a99 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/back_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/badusb_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/badusb_10px.png new file mode 100644 index 000000000..037474aa3 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/badusb_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/dir_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/dir_10px.png new file mode 100644 index 000000000..a4cdf453e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/dir_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/iButtonDolphinVerySuccess_108x52.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/iButtonDolphinVerySuccess_108x52.png new file mode 100644 index 000000000..2b4bec7c6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/iButtonDolphinVerySuccess_108x52.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/iButtonKey_49x44.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/iButtonKey_49x44.png new file mode 100644 index 000000000..db895ec52 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/iButtonKey_49x44.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ibutt_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ibutt_10px.png new file mode 100644 index 000000000..2fdaf123a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ibutt_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ir_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ir_10px.png new file mode 100644 index 000000000..22c986180 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/ir_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/loading_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/loading_10px.png new file mode 100644 index 000000000..4f626b3d5 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/loading_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/music_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/music_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_bad1_46x49.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_bad1_46x49.png new file mode 100644 index 000000000..9b0e7c74e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_bad1_46x49.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_bad2_46x49.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_bad2_46x49.png new file mode 100644 index 000000000..d11682ab8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_bad2_46x49.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_bad3_46x49.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_bad3_46x49.png new file mode 100644 index 000000000..e39e6629d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_bad3_46x49.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_bottom_128x18.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_bottom_128x18.png new file mode 100644 index 000000000..691ed8b4a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_bottom_128x18.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_happy1_46x49.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_happy1_46x49.png new file mode 100644 index 000000000..56ea000cd Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_happy1_46x49.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_happy2_46x49.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_happy2_46x49.png new file mode 100644 index 000000000..f64e770e5 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_happy2_46x49.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_happy3_46x49.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_happy3_46x49.png new file mode 100644 index 000000000..7aef17674 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_happy3_46x49.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_left_6x46.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_left_6x46.png new file mode 100644 index 000000000..17d3ad265 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_left_6x46.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_okay1_46x49.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_okay1_46x49.png new file mode 100644 index 000000000..198ba5436 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_okay1_46x49.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_okay2_46x49.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_okay2_46x49.png new file mode 100644 index 000000000..34fd3767b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_okay2_46x49.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_okay3_46x49.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_okay3_46x49.png new file mode 100644 index 000000000..e65da5b0e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/passport_okay3_46x49.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/sub1_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/sub1_10px.png new file mode 100644 index 000000000..5a25fdf4e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/sub1_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/u2f_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/u2f_10px.png new file mode 100644 index 000000000..fcd87a2ef Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/u2f_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/unknown_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/unknown_10px.png new file mode 100644 index 000000000..18d31c67c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/unknown_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/update_10px.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/update_10px.png new file mode 100644 index 000000000..5a97651c4 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/assets/update_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/etch-a-sketch-icon.png b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/etch-a-sketch-icon.png new file mode 100644 index 000000000..567c16900 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/etch-a-sketch-icon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/etch-a-sketch.c b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/etch-a-sketch.c new file mode 100644 index 000000000..ae8337da0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/etch-a-sketch.c @@ -0,0 +1,271 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Header-file for boolean data-type. +#include +#include + +#define WIDTH 64 +#define HEIGHT 32 + +const int brush_size = 2; + +typedef struct selected_position { + int x; + int y; +} selected_position; + +typedef struct { + selected_position selected; + bool board[64][32]; + bool isDrawing; + bool showWelcome; +} EtchData; + +// Sequence to indicate that drawing is enabled. +const NotificationSequence sequence_begin_draw = { + &message_display_backlight_on, + + // Vibrate to indicate that drawing is enabled. + &message_vibro_on, + &message_note_g5, + &message_delay_50, + &message_note_c6, + &message_delay_50, + &message_note_e5, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +// sequence to indicate that drawing is disabled +const NotificationSequence sequence_end_draw = { + &message_red_0, + // Indicate that drawing is disabled. + &message_vibro_on, + &message_note_g5, + &message_delay_50, + &message_note_e5, + &message_delay_50, + &message_vibro_off, + &message_sound_off, + &message_do_not_reset, + NULL, +}; + +// Indicate that drawing is enabled. +const NotificationSequence sequence_draw_enabled = { + &message_red_255, + &message_do_not_reset, + NULL, +}; + +// Indicate that drawing is disabled. +const NotificationSequence sequence_draw_disabled = { + &message_red_0, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_cleanup = { + &message_red_0, + &message_green_0, + &message_blue_0, + &message_sound_off, + &message_vibro_off, + NULL, +}; + +void etch_draw_callback(Canvas* canvas, void* ctx) { + const EtchData* etch_state = acquire_mutex((ValueMutex*)ctx, 25); + UNUSED(ctx); + + canvas_clear(canvas); + + // Show Welcome Message + if(etch_state->showWelcome) { + // Draw Etch A Sketch frame + canvas_draw_frame(canvas, 5, 3, 119, 55); // Border + canvas_draw_icon(canvas, 8, 50, &I_Ok_btn_pressed_13x13); // Left Knob + canvas_draw_icon(canvas, 107, 50, &I_Ok_btn_pressed_13x13); // Right Knob + + // Draw Etch A Sketch text banner + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 36, 15, "Etch A Sketch"); + + // Draw Etch A Sketch instructions "Hold Back to clear" + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 31, 26, "* Hold "); + canvas_draw_icon(canvas, 59, 18, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 72, 26, "to clear"); + + // Draw Etch A Sketch instructions "Hold OK button to draw" + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 31, 37, "* Hold"); + canvas_draw_icon(canvas, 61, 30, &I_ButtonCenter_7x7); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 72, 37, "to draw"); + } + + canvas_set_color(canvas, ColorBlack); + //draw the canvas(64x32) on screen(144x64) using brush_size*brush_size tiles + for(int y = 0; y < 32; y++) { + for(int x = 0; x < 64; x++) { + if(etch_state->board[x][y]) { + canvas_draw_box(canvas, x * brush_size, y * brush_size, 2, 2); + } + } + } + + //draw cursor as a brush_size by brush_size black box + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, + etch_state->selected.x * brush_size, + etch_state->selected.y * brush_size, + brush_size, + brush_size); + + //release the mutex + release_mutex((ValueMutex*)ctx, etch_state); +} + +void etch_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t etch_a_sketch_app(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + EtchData* etch_state = malloc(sizeof(EtchData)); + ValueMutex etch_state_mutex; + if(!init_mutex(&etch_state_mutex, etch_state, sizeof(EtchData))) { + FURI_LOG_E("etch", "cannot create mutex\r\n"); + free(etch_state); + return -1; + } + + // Configure view port + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, etch_draw_callback, &etch_state_mutex); + view_port_input_callback_set(view_port, etch_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + InputEvent event; + + // Show Welcome Banner + etch_state->showWelcome = true; + + while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { + //break out of the loop if the back key is pressed + if(event.key == InputKeyBack && event.type == InputTypeShort) { + break; + } + + // Clear + // TODO: Do animation of shaking board + if(event.key == InputKeyBack && event.type == InputTypeLong) { + etch_state->showWelcome = false; + etch_state->board[1][1] = true; + for(int y = 0; y < 32; y++) { + for(int x = 0; x < 64; x++) { + etch_state->board[x][y] = false; + } + } + view_port_update(view_port); + } + + // Keep LED on while drawing + if(etch_state->isDrawing) { + notification_message(notification, &sequence_draw_enabled); + } else { + notification_message(notification, &sequence_draw_disabled); + } + + // Single Dot Select + if(event.key == InputKeyOk && event.type == InputTypeShort) { + etch_state->board[etch_state->selected.x][etch_state->selected.y] = + !etch_state->board[etch_state->selected.x][etch_state->selected.y]; + } + + // Start Drawing + if(event.key == InputKeyOk && event.type == InputTypeLong) { + // notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_begin_draw); + notification_message(notification, &sequence_begin_draw); + + if(etch_state->isDrawing) { + // We're ending the drawing + notification_message(notification, &sequence_end_draw); + } + + etch_state->isDrawing = !etch_state->isDrawing; + etch_state->board[etch_state->selected.x][etch_state->selected.y] = true; + + view_port_update(view_port); + } + + //check the key pressed and change x and y accordingly + if(event.type == InputTypeShort || event.type == InputTypeRepeat || + event.type == InputTypeLong) { + switch(event.key) { + case InputKeyUp: + etch_state->selected.y -= 1; + break; + case InputKeyDown: + etch_state->selected.y += 1; + break; + case InputKeyLeft: + etch_state->selected.x -= 1; + break; + case InputKeyRight: + etch_state->selected.x += 1; + break; + default: + break; + } + + //check if cursor position is out of bounds and reset it to the closest position + if(etch_state->selected.x < 0) { + etch_state->selected.x = 0; + } + if(etch_state->selected.x > 61) { + etch_state->selected.x = 61; + } + if(etch_state->selected.y < 0) { + etch_state->selected.y = 0; + } + if(etch_state->selected.y > 31) { + etch_state->selected.y = 31; + } + if(etch_state->isDrawing == true) { + etch_state->board[etch_state->selected.x][etch_state->selected.y] = true; + } + view_port_update(view_port); + } + } + + notification_message(notification, &sequence_cleanup); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + free(etch_state); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/etch-a-sketch_icons.c b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/etch-a-sketch_icons.c new file mode 100644 index 000000000..94ddf376b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/etch-a-sketch/etch-a-sketch_icons.c @@ -0,0 +1,4324 @@ +#include "assets_icons.h" + +#include + +const uint8_t _I_Certification1_103x56_0[] = { + 0x01, 0x00, 0x97, 0x01, 0x9f, 0xff, 0xbe, 0x30, 0x38, 0x04, 0xf2, 0x01, 0xe0, 0x80, 0x82, 0x87, + 0xf9, 0x01, 0x06, 0x24, 0xfe, 0x01, 0xf8, 0x80, 0xfe, 0x21, 0xff, 0xf8, 0x3c, 0xff, 0x9c, 0x0c, + 0x1e, 0x00, 0x30, 0x7f, 0xc0, 0xc1, 0xe3, 0xc0, 0xe3, 0xd0, 0x7e, 0x75, 0xc4, 0x46, 0x30, 0x70, + 0xd9, 0x46, 0x3c, 0x10, 0x09, 0xc0, 0x30, 0xfe, 0x10, 0x1c, 0x04, 0x3c, 0x18, 0x37, 0x08, 0x05, + 0xc0, 0x18, 0x77, 0x88, 0x07, 0x00, 0x6e, 0x31, 0x89, 0x87, 0xe2, 0x00, 0x0c, 0x39, 0xc0, 0x30, + 0x49, 0x83, 0x18, 0x8c, 0x7f, 0xa0, 0x60, 0xc3, 0x2c, 0xa0, 0x30, 0x60, 0xe0, 0x01, 0x06, 0x14, + 0x70, 0x18, 0x26, 0x51, 0x8c, 0x43, 0x20, 0x70, 0x20, 0x64, 0xe3, 0x03, 0xa2, 0x74, 0x10, 0x62, + 0x5f, 0xce, 0xc3, 0x8f, 0x06, 0x78, 0x31, 0xc4, 0xc6, 0x33, 0xc2, 0x6f, 0x99, 0xf5, 0x03, 0x89, + 0xb7, 0xb0, 0x2d, 0x7d, 0x9f, 0x2e, 0x98, 0x8c, 0x0a, 0x86, 0x3c, 0x0c, 0x30, 0xb9, 0x7e, 0x20, + 0x30, 0x88, 0x07, 0xfe, 0x0e, 0x0c, 0x42, 0xda, 0x40, 0x3f, 0x80, 0x32, 0x00, 0x78, 0x03, 0xc0, + 0x0b, 0x0c, 0xe6, 0x14, 0xc4, 0x58, 0x34, 0x00, 0x40, 0xa7, 0xc8, 0x18, 0x46, 0xd1, 0xc1, 0x7f, + 0xe4, 0x00, 0x81, 0x3d, 0x08, 0x30, 0xc9, 0x63, 0x64, 0x80, 0x44, 0xc1, 0x03, 0xf8, 0x2a, 0x88, + 0x30, 0xa1, 0xc7, 0xd1, 0x02, 0x88, 0x20, 0x63, 0x88, 0x71, 0x06, 0x10, 0x10, 0x7e, 0x06, 0xf1, + 0x04, 0x14, 0x18, 0x18, 0xa3, 0x11, 0x81, 0x02, 0x06, 0x1c, 0x21, 0x11, 0x32, 0x58, 0x20, 0x02, + 0xf2, 0x83, 0xbc, 0x04, 0x80, 0x9a, 0x0c, 0x00, 0x64, 0x70, 0x1f, 0x39, 0xca, 0x44, 0xb9, 0xe5, + 0xa1, 0x90, 0x5f, 0x41, 0x85, 0x09, 0x99, 0x14, 0x4b, 0x8e, 0x6d, 0x19, 0x04, 0x88, 0x18, 0x3c, + 0x08, 0x28, 0x50, 0x40, 0x09, 0x40, 0x8a, 0x1c, 0x07, 0xc1, 0x02, 0xf4, 0x03, 0x1a, 0x10, 0x0c, + 0x1b, 0x84, 0x07, 0xa8, 0x04, 0x1f, 0xc6, 0x3e, 0xfe, 0x19, 0x49, 0x84, 0x47, 0x24, 0x04, 0x12, + 0x00, 0xff, 0xc1, 0x83, 0x22, 0x03, 0x8c, 0xe2, 0x23, 0x91, 0x2f, 0x8c, 0x33, 0x81, 0xc0, 0x06, + 0x0d, 0x04, 0x18, 0x31, 0xcd, 0x0c, 0x40, 0xfa, 0x20, 0xc3, 0xc0, 0x60, 0x10, 0x05, 0x61, 0x88, + 0x1f, 0x44, 0x18, 0x7c, 0x0c, 0x00, 0x64, 0x60, 0xd0, 0x28, 0x42, 0xb4, 0x41, 0x82, 0x33, 0x1d, + 0x07, 0x00, 0x5c, 0x30, 0x78, 0x24, 0x11, 0x1e, 0x20, 0xc3, 0xc8, 0x48, 0x20, 0xd8, 0x08, 0x1c, + 0x22, 0x05, 0x38, 0x13, 0x26, 0x23, 0x41, 0x0a, 0x14, 0x0b, 0xa4, 0x66, 0x05, 0xa2, 0x06, 0x0c, + 0x12, 0x7f, 0x12, 0x0c, 0x89, 0x0c, 0x43, 0x23, 0xf8, 0x06, 0x14, 0x0b, 0x81, 0x21, 0x80, 0x7f, + 0xc0, 0x03, 0x1f, 0x47, 0x21, 0x2c, 0x68, 0x10, 0x0f, 0xf0, 0x30, 0x61, 0xe9, 0xc5, 0x00, 0x78, + 0x03, 0xc0, 0x0b, 0xfc, 0x1d, 0xb0, 0x33, 0xc1, 0x9e, 0x0c, 0x60, +}; +const uint8_t* const _I_Certification1_103x56[] = {_I_Certification1_103x56_0}; + +const uint8_t _I_Certification2_98x33_0[] = { + 0x01, 0x00, 0xd6, 0x00, 0x80, 0x78, 0x21, 0xe0, 0xca, 0xfe, 0x5f, 0xc1, 0x95, 0x1f, 0xf8, 0x40, + 0x41, 0x71, 0x80, 0x81, 0x80, 0x44, 0x19, 0x1c, 0x00, 0x10, 0xc1, 0x81, 0x91, 0x60, 0x01, 0x0c, + 0x30, 0x19, 0x13, 0x00, 0x10, 0xc6, 0x01, 0x90, 0x09, 0x20, 0x79, 0x00, 0x18, 0x46, 0x42, 0x92, + 0x06, 0x57, 0xf2, 0x7e, 0x0c, 0x68, 0x37, 0x82, 0x07, 0xc0, 0x82, 0x48, 0x01, 0x8f, 0xfe, 0x81, + 0xfc, 0x01, 0x91, 0xe6, 0x8f, 0xc0, 0xff, 0xfe, 0x49, 0x08, 0x31, 0xbf, 0xf0, 0x1f, 0x9c, 0x3e, + 0xc9, 0x26, 0xc7, 0x47, 0x28, 0x1d, 0xff, 0x3c, 0xe2, 0x05, 0x1f, 0x07, 0x0c, 0x00, 0x18, 0x3d, + 0x19, 0x15, 0x82, 0xa1, 0x10, 0xa2, 0xc0, 0xd3, 0x02, 0xc2, 0x10, 0x60, 0xe4, 0x26, 0x54, 0x0e, + 0x84, 0x4e, 0x8b, 0x02, 0x52, 0x9f, 0x92, 0x47, 0x88, 0xd5, 0x53, 0xe1, 0x1e, 0x4b, 0xc8, 0x31, + 0x03, 0x23, 0x84, 0x93, 0x50, 0x21, 0x10, 0x4a, 0x08, 0x30, 0x7f, 0xe4, 0x21, 0x06, 0x0e, 0x0a, + 0x45, 0x40, 0xa2, 0x45, 0x04, 0xc4, 0x0c, 0x4c, 0xf9, 0x34, 0x70, 0x9e, 0x32, 0x68, 0xb3, 0x09, + 0x03, 0x40, 0xcc, 0x13, 0x0f, 0x05, 0x51, 0x10, 0xc0, 0x60, 0xd8, 0x58, 0x53, 0x80, 0x81, 0xb3, + 0xe6, 0x30, 0x0c, 0x17, 0x4c, 0xcc, 0x01, 0x82, 0xf9, 0x20, 0x03, 0x58, 0x01, 0x82, 0xec, 0x20, + 0x03, 0x70, 0x0b, 0xa8, 0x00, 0x60, 0xc0, 0x80, 0x44, 0x19, 0x10, 0x08, 0xff, 0xc2, 0x02, 0x0c, + 0x9f, 0xe5, 0xfc, 0x17, 0x30, 0x0f, 0x03, 0x70, 0x40, 0x10, +}; +const uint8_t* const _I_Certification2_98x33[] = {_I_Certification2_98x33_0}; + +const uint8_t _A_Levelup1_128x64_0[] = { + 0x01, 0x00, 0x22, 0x01, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, 0x38, 0x1f, 0xe0, + 0x1d, 0x97, 0xff, 0xe7, 0x1c, 0x1f, 0x99, 0xf8, 0x1c, 0xe0, 0x1f, 0x96, 0x78, 0x07, 0x03, 0xf8, + 0x0f, 0xb8, 0xdc, 0x02, 0x01, 0xfb, 0x07, 0xdc, 0x14, 0x1e, 0x3f, 0x40, 0x7d, 0xc0, 0x81, 0xe3, + 0xe8, 0x2f, 0x5c, 0x04, 0x1e, 0x3d, 0x00, 0x7d, 0x6f, 0xc1, 0xc3, 0x00, 0xd0, 0x03, 0xea, 0xbe, + 0x0f, 0xf0, 0x79, 0x50, 0x01, 0xf5, 0x2f, 0x07, 0xf8, 0x38, 0x63, 0xf8, 0x0f, 0xf1, 0x7a, 0x62, + 0x10, 0x0c, 0x04, 0x80, 0x1f, 0x59, 0xf8, 0xf8, 0xb4, 0x6f, 0x80, 0xfa, 0xb7, 0xcc, 0x20, 0x12, + 0x0f, 0x34, 0x03, 0xfa, 0x0f, 0x88, 0xbd, 0x00, 0x1e, 0x3e, 0x30, 0x7d, 0x42, 0xf0, 0x10, 0x18, + 0x87, 0x9c, 0x1f, 0x70, 0x08, 0x16, 0x43, 0xfe, 0x0f, 0xaf, 0x5c, 0x02, 0x18, 0x0f, 0xc8, 0xdc, + 0x0e, 0x20, 0x0f, 0xc8, 0x5c, 0x31, 0x7a, 0x37, 0xf0, 0x7d, 0xa3, 0xc6, 0x81, 0x3f, 0x37, 0xdc, + 0x82, 0x01, 0xc0, 0x49, 0x09, 0xff, 0x61, 0x00, 0xfa, 0x97, 0x9f, 0xc6, 0x00, 0x32, 0x10, 0x7d, + 0x40, 0x20, 0xc0, 0xfc, 0xaf, 0xc0, 0x23, 0x0f, 0x01, 0x07, 0xd6, 0xfe, 0x01, 0xe0, 0x07, 0xe5, + 0xfc, 0x7c, 0x30, 0xf0, 0x7d, 0xff, 0xe0, 0x41, 0xe2, 0x07, 0xdc, 0x14, 0x1f, 0xd0, 0xd0, 0x7f, + 0x45, 0x45, 0xfe, 0x0f, 0xf0, 0x7f, 0x17, 0xd3, 0xf4, 0x7f, 0x98, 0x18, 0x3f, 0xc1, 0xe7, 0xf4, + 0x57, 0xc8, 0xbd, 0x01, 0xff, 0xe8, 0x6f, 0xc8, 0x3d, 0x3a, 0x0f, 0xf9, 0x67, 0x88, 0x00, 0xb4, + 0x00, 0xf8, 0x8b, 0x83, 0xd2, 0x80, 0x0f, 0x88, 0x48, 0x3d, 0x30, 0x1f, 0xc0, 0x6a, 0xe3, 0xef, + 0xf0, 0x60, 0x7a, 0x40, 0x3e, 0x60, 0xf5, 0xbb, 0xe0, 0x20, 0x20, 0xf4, 0x80, 0xf3, 0x01, 0xeb, + 0x8b, 0xbf, 0x82, 0x84, 0x1e, 0x93, 0x0c, 0x00, 0x3d, 0x21, 0x60, 0xa2, 0x07, 0xa7, 0x02, 0xfe, + 0x0f, 0xd2, 0x88, 0xff, 0x81, 0xe6, +}; +const uint8_t _A_Levelup1_128x64_1[] = { + 0x01, 0x00, 0x02, 0x02, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x2f, 0xf8, 0x17, 0x59, 0xfe, 0xf8, 0x6b, 0x37, + 0xf9, 0xf0, 0x32, 0x7e, 0xc1, 0xac, 0x02, 0x19, 0xff, 0xfe, 0x03, 0xfc, 0x11, 0x5c, 0xfe, 0x7f, + 0xfe, 0xf1, 0x8c, 0x2e, 0x3d, 0xce, 0x80, 0xe7, 0x3e, 0xff, 0x90, 0x7c, 0xc3, 0xe6, 0x10, 0x0b, + 0x07, 0xfb, 0xf0, 0x61, 0x8c, 0x7c, 0x3e, 0x19, 0x00, 0xc0, 0x43, 0x6f, 0xc1, 0xe8, 0xbf, 0xe3, + 0xa0, 0x50, 0x08, 0x04, 0x0a, 0xfe, 0x83, 0x7f, 0xdf, 0xf6, 0x09, 0x01, 0x07, 0x8e, 0x11, 0x25, + 0x1f, 0x7f, 0x7e, 0x00, 0x1c, 0x30, 0x09, 0x41, 0xfd, 0xc0, 0x03, 0x1f, 0xa0, 0x03, 0xca, 0x21, + 0xfe, 0x30, 0x45, 0xfe, 0x90, 0x0f, 0x3f, 0xe7, 0xf4, 0x1e, 0xff, 0x79, 0x00, 0x3c, 0xa4, 0x1b, + 0xc4, 0xf4, 0xca, 0x01, 0xe5, 0xa0, 0xfc, 0x03, 0xe6, 0x20, 0x0f, 0x2a, 0x00, 0x3c, 0x7c, 0xef, + 0x96, 0xbf, 0xcc, 0x81, 0xc3, 0xf8, 0x07, 0x93, 0x0d, 0xaf, 0xbf, 0xf5, 0x70, 0xc0, 0x20, 0x94, + 0x1d, 0xc4, 0x3f, 0xf9, 0xef, 0x08, 0x2f, 0xf0, 0x7e, 0x5e, 0x2c, 0x17, 0xf3, 0xee, 0x6f, 0x89, + 0xc0, 0x3c, 0xb0, 0x7f, 0xc7, 0x93, 0x83, 0x87, 0xf1, 0x07, 0x80, 0x79, 0x70, 0xbf, 0x80, 0x7b, + 0xc9, 0xe0, 0x13, 0x86, 0xf8, 0xff, 0x70, 0xbc, 0x0f, 0x80, 0x3d, 0xb0, 0xfa, 0x18, 0xe1, 0x40, + 0x7f, 0xb0, 0x58, 0x4f, 0xf3, 0xff, 0xdf, 0x08, 0x2c, 0x0e, 0x18, 0xc0, 0x10, 0x78, 0xc0, 0x7f, + 0xdb, 0xfd, 0xf8, 0x3d, 0x74, 0x70, 0x4c, 0x04, 0x07, 0xfb, 0x03, 0x84, 0xe3, 0xfe, 0xe0, 0xf6, + 0x87, 0xf9, 0x20, 0x10, 0x6f, 0xd4, 0x0b, 0x03, 0xe7, 0xf9, 0xf7, 0xce, 0x47, 0x7b, 0xbf, 0x67, + 0xe2, 0x90, 0x38, 0x05, 0xfe, 0x03, 0xc9, 0xff, 0xfe, 0xfd, 0xee, 0x23, 0x9f, 0xff, 0x27, 0xd0, + 0xc2, 0xf8, 0xfc, 0x20, 0x18, 0x16, 0x6b, 0xff, 0xf3, 0xf0, 0xe6, 0x81, 0xca, 0x7c, 0x00, 0xf5, + 0x22, 0x0c, 0x02, 0x15, 0x20, 0x07, 0x94, 0x04, 0x1e, 0xd7, 0xf8, 0xfe, 0x82, 0x14, 0x6f, 0x10, + 0x00, 0xea, 0x51, 0xe3, 0xf3, 0x08, 0x26, 0x7a, 0x03, 0x12, 0x37, 0x88, 0x7c, 0x91, 0xe3, 0xe7, + 0x3f, 0xb4, 0x48, 0xde, 0x21, 0xf2, 0xff, 0x7f, 0xf9, 0xe7, 0xf6, 0x91, 0x40, 0x2f, 0x01, 0xf2, + 0x07, 0xba, 0x58, 0x68, 0x43, 0xe2, 0x0f, 0x88, 0xbc, 0x7d, 0xff, 0xb0, 0x79, 0xf3, 0xe1, 0x78, + 0x20, 0x79, 0xc0, 0x47, 0xc2, 0x8f, 0x43, 0xf0, 0xe8, 0xe0, 0x44, 0xe2, 0x0f, 0x8f, 0xdf, 0xc6, + 0xae, 0x35, 0x28, 0xb7, 0xc6, 0x05, 0x02, 0x07, 0x97, 0x07, 0xff, 0xf1, 0x17, 0x20, 0x10, 0x74, + 0xe0, 0xe3, 0x80, 0x72, 0x67, 0xff, 0x82, 0xf2, 0xfc, 0xe1, 0xec, 0xe1, 0x00, 0x15, 0x0a, 0x1e, + 0x60, 0x12, 0x72, 0x70, 0xe5, 0xe0, 0xf4, 0x85, 0x41, 0x83, 0xcb, 0x37, 0x83, 0xe1, 0xc5, 0xe9, + 0x04, 0x81, 0x07, 0xc7, 0xde, 0x0f, 0x2a, 0x5c, 0x3b, 0x40, 0x0f, 0x4d, 0x24, 0x83, 0x3d, 0xbf, + 0x30, 0x1e, 0x2e, 0x39, 0x40, 0x07, 0xa4, 0x22, 0x01, 0x67, 0xcf, 0x8b, 0xce, 0x17, 0x83, 0x91, + 0x03, 0xd2, 0x61, 0x2a, 0xcc, 0xf3, 0x81, 0xe9, 0x1c, 0x8c, 0x03, 0xd3, 0x81, 0x00, 0xc3, 0x18, + 0x12, 0x2f, 0xe0, 0x83, 0x83, 0xd2, 0x01, 0xff, 0xc0, 0x83, 0xd4, 0x12, 0x20, 0xf5, 0x80, 0x60, + 0x01, 0xe2, 0x50, 0x29, 0x78, 0x3d, 0x98, 0x63, 0x40, 0x48, 0xa0, 0x3e, 0xac, 0xc0, 0xf3, 0xa0, + 0x83, 0xe2, 0x41, 0xc0, 0x3f, 0x9e, 0x04, 0x1f, 0x3e, 0x0f, 0xc8, 0x80, 0xa0, 0x60, 0x81, 0x07, + 0xb6, 0x42, 0x85, 0xfc, 0x0f, 0x90, +}; +const uint8_t _A_Levelup1_128x64_2[] = { + 0x01, 0x00, 0xc1, 0x02, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x3f, 0xe4, 0x1f, 0xdf, 0x40, + 0x43, 0xf3, 0xc0, 0xa9, 0x7f, 0xfb, 0x03, 0xdf, 0x66, 0x05, 0x4d, 0xff, 0xbc, 0x1e, 0xf3, 0xbf, + 0xf0, 0x14, 0xe3, 0xf7, 0x8f, 0xf7, 0xc4, 0x1e, 0xb1, 0x3f, 0xe0, 0x14, 0xe1, 0xf9, 0x02, 0x0b, + 0xc5, 0xc1, 0xeb, 0xcc, 0xff, 0x03, 0xda, 0x1e, 0x0f, 0x48, 0x3f, 0xbf, 0xfd, 0xf8, 0x07, 0xad, + 0xfc, 0x1e, 0x57, 0xff, 0xf4, 0x0f, 0xe7, 0xff, 0x07, 0xaf, 0x8e, 0xff, 0xf8, 0xed, 0xc7, 0xee, + 0x1f, 0xc8, 0x18, 0x34, 0x41, 0xeb, 0xc3, 0x9f, 0xfc, 0x3f, 0x70, 0xfe, 0x07, 0xfe, 0x01, 0x9c, + 0x0b, 0x88, 0x88, 0x41, 0xe7, 0x1f, 0xc8, 0x5f, 0xe0, 0x7c, 0x08, 0x7c, 0x03, 0xf9, 0x7f, 0x9e, + 0x07, 0xd3, 0x8f, 0x17, 0x80, 0x7e, 0xe0, 0x2f, 0x71, 0x85, 0x7f, 0xa7, 0xf1, 0xe0, 0x7a, 0x63, + 0xe7, 0xf0, 0x09, 0x99, 0x34, 0x42, 0x03, 0xfb, 0x9f, 0xa5, 0xd3, 0xf1, 0x3f, 0xbf, 0xc1, 0x38, + 0x10, 0x1c, 0xe6, 0xaa, 0x04, 0x1f, 0x10, 0xf2, 0x7e, 0x26, 0xff, 0x88, 0x74, 0xc2, 0x01, 0x60, + 0x0f, 0x8d, 0xfc, 0x5c, 0x2c, 0x23, 0xff, 0x4c, 0xed, 0x18, 0xe8, 0x64, 0x03, 0x01, 0xa9, 0xc1, + 0x5e, 0xf3, 0xc1, 0xf4, 0x9f, 0xcc, 0xf8, 0x10, 0x48, 0x10, 0x74, 0x60, 0x58, 0x0b, 0xfe, 0x32, + 0xfe, 0xbe, 0x50, 0x78, 0x97, 0x8b, 0x49, 0xc0, 0xf7, 0xe0, 0x14, 0xc4, 0xbe, 0x3c, 0x06, 0x00, + 0x1c, 0x30, 0x08, 0x46, 0x02, 0x78, 0x0f, 0x20, 0x01, 0xdf, 0xb0, 0x14, 0x00, 0x79, 0x64, 0x21, + 0x41, 0x11, 0x07, 0x23, 0x30, 0x60, 0x00, 0xf3, 0x88, 0x00, 0x87, 0x81, 0xbf, 0x07, 0x9e, 0x73, + 0xdf, 0x01, 0x90, 0x03, 0xcb, 0x40, 0x53, 0x19, 0x02, 0x3d, 0x23, 0x3f, 0xf0, 0x7a, 0x00, 0xc8, + 0x1e, 0x5c, 0x70, 0x78, 0xc6, 0xb9, 0xd0, 0x11, 0x59, 0x0c, 0xc7, 0x51, 0x0a, 0x80, 0x03, 0x07, + 0x16, 0x02, 0x13, 0xc0, 0x83, 0xd4, 0x04, 0x38, 0xef, 0xff, 0x8f, 0xfa, 0x0f, 0x18, 0x3a, 0x6c, + 0x43, 0x22, 0x00, 0x1c, 0x06, 0xaa, 0x14, 0xd9, 0x1f, 0x38, 0xfc, 0xa1, 0xa4, 0x81, 0x84, 0x03, + 0xcc, 0x02, 0x3f, 0xf0, 0x71, 0x80, 0x06, 0x02, 0x1f, 0x5d, 0x14, 0x2b, 0xf0, 0x0f, 0x51, 0x80, + 0xc1, 0x01, 0xe5, 0x00, 0x0b, 0x0f, 0xe7, 0xf6, 0x0f, 0x5c, 0x50, 0x3d, 0x35, 0x3e, 0x07, 0x59, + 0x03, 0x8a, 0xff, 0x0f, 0x80, 0x51, 0x13, 0xe7, 0x01, 0xf8, 0x7b, 0xff, 0xa0, 0xf2, 0x87, 0xff, + 0xe3, 0xd2, 0x8e, 0x20, 0x3c, 0xa3, 0x1a, 0x9f, 0x87, 0x1e, 0xff, 0xde, 0xc7, 0x8b, 0x47, 0xd2, + 0x1f, 0x10, 0x7a, 0x07, 0xc7, 0x8f, 0x3f, 0xff, 0x61, 0xff, 0xff, 0x7e, 0xfc, 0x1f, 0x40, 0x1f, + 0x5f, 0x07, 0x9d, 0xfa, 0x3e, 0x37, 0xf9, 0x87, 0xc6, 0x02, 0x0f, 0x18, 0xe4, 0x07, 0x80, 0x0c, + 0x24, 0xf9, 0x14, 0x04, 0x3e, 0x31, 0xf7, 0x58, 0xc7, 0xf5, 0x38, 0x0f, 0x1b, 0xbd, 0x00, 0x03, + 0x00, 0xc2, 0x03, 0xca, 0x1e, 0x23, 0x1f, 0x80, 0x28, 0x73, 0xe0, 0x21, 0xf9, 0xff, 0x70, 0x30, + 0x41, 0x90, 0xe0, 0x20, 0xfa, 0x98, 0x17, 0xf3, 0xee, 0x0f, 0x2f, 0xe7, 0x83, 0xdf, 0x01, 0x4f, + 0xac, 0x03, 0xfc, 0x0f, 0x10, 0xe8, 0xa4, 0xc9, 0x5e, 0xba, 0x96, 0xf8, 0x87, 0x87, 0xcf, 0xf1, + 0xff, 0xf1, 0xe7, 0x80, 0xf1, 0x88, 0x3f, 0x88, 0x3e, 0x3f, 0xfe, 0x7f, 0x7f, 0xcf, 0xf9, 0xff, + 0xc0, 0xa3, 0x1a, 0x08, 0x24, 0x82, 0x00, 0x01, 0x1d, 0x4e, 0x0b, 0x3f, 0xc7, 0xf3, 0xfc, 0xff, + 0x9f, 0xf9, 0x08, 0xa8, 0x41, 0xe8, 0xdf, 0x18, 0x7f, 0x07, 0xcf, 0x78, 0x1e, 0x8d, 0xfe, 0x00, + 0x1f, 0x5c, 0x0c, 0x2a, 0x0f, 0xe0, 0xbb, 0xc7, 0x0f, 0xc7, 0x00, 0x83, 0x0f, 0x44, 0x82, 0x30, + 0x30, 0x38, 0xf8, 0x22, 0x78, 0x00, 0x70, 0xf3, 0xf0, 0x1a, 0xa0, 0x7a, 0x60, 0xe1, 0x50, 0xaf, + 0x84, 0x07, 0xc0, 0x0f, 0x1d, 0xb8, 0x5c, 0x60, 0xbc, 0x06, 0x70, 0x81, 0x06, 0x01, 0x60, 0xf0, + 0x28, 0x85, 0x64, 0x34, 0xad, 0x46, 0x2a, 0x09, 0x80, 0xfc, 0xc3, 0x20, 0x18, 0x9d, 0x56, 0x48, + 0x32, 0x3f, 0x80, 0xf8, 0xe2, 0xa0, 0x36, 0x00, 0x78, 0xc1, 0xd6, 0x23, 0x31, 0x80, 0x63, 0x01, + 0xe3, 0xb8, 0xfd, 0xff, 0xe1, 0x10, 0xe8, 0xc2, 0x7c, 0x56, 0x45, 0xc1, 0xc0, 0x60, 0xe0, 0x52, + 0xae, 0x41, 0xb8, 0x61, 0x1f, 0x08, 0x38, 0x30, 0x60, 0xc5, 0xd1, 0x80, 0xdf, 0xf0, 0x06, 0x12, + 0x17, 0x89, 0x64, 0x57, 0xe5, 0xf8, 0x60, 0x9d, 0x03, 0x44, 0x61, 0x10, 0x7b, 0x00, 0x10, 0xea, + 0x25, 0xf1, 0x07, 0x8a, 0xbc, 0xc1, 0xeb, 0xa0, 0xd5, 0x42, 0x97, 0xc3, 0xff, 0x54, 0x88, 0x3d, + 0xc1, 0x25, 0xfe, 0x5f, 0x88, 0x94, 0x80, 0x0d, 0x90, 0xd5, 0x4a, 0x81, 0xc3, 0xfe, 0xce, 0x03, + 0xe2, 0x10, 0x1f, 0x2f, 0xff, 0xe2, 0x7f, 0x01, 0x06, 0xa8, 0x18, 0x74, 0x83, 0xf1, 0xfb, 0x7f, + 0xff, 0x33, 0xf8, 0x30, 0xfd, 0x41, 0xe2, 0xe1, 0x25, 0x79, 0x41, 0xcf, 0x62, 0x0f, 0x5f, 0xc6, + 0xa3, 0x55, 0x8a, 0x07, 0x90, +}; +const uint8_t _A_Levelup1_128x64_3[] = { + 0x01, 0x00, 0x0a, 0x03, 0x8f, 0xc0, 0xb8, 0x1f, 0xfb, 0xfd, 0xe2, 0x7f, 0xf8, 0x02, 0x0f, 0xff, + 0xff, 0x03, 0xff, 0x00, 0xc6, 0xff, 0x36, 0xe0, 0x47, 0xfd, 0xff, 0x08, 0xff, 0xf3, 0xff, 0x3f, + 0x05, 0x8f, 0xf0, 0x1e, 0x5f, 0xf8, 0xfe, 0x02, 0xfb, 0xff, 0xf8, 0x43, 0xff, 0x99, 0xfd, 0xf8, + 0x44, 0x81, 0xe7, 0x17, 0x88, 0x7d, 0x37, 0xe0, 0xf2, 0x87, 0xe7, 0xff, 0xe0, 0x11, 0xfe, 0x83, + 0xca, 0x1f, 0x20, 0xf8, 0x0f, 0x46, 0x0f, 0xfe, 0x83, 0xf6, 0xff, 0xfc, 0xf0, 0xfa, 0x47, 0xec, + 0x1e, 0x08, 0xf8, 0x3c, 0xa0, 0x7c, 0x3f, 0xff, 0x9c, 0x1e, 0x93, 0xf8, 0x06, 0x02, 0x1f, 0xf8, + 0x2c, 0x8c, 0x07, 0xc1, 0xff, 0xfd, 0x83, 0xdb, 0xc1, 0x07, 0xfc, 0x40, 0x6f, 0xda, 0x0d, 0x47, + 0xff, 0xf2, 0x0f, 0x4b, 0xf8, 0x7c, 0x60, 0xda, 0x88, 0x0c, 0x72, 0x01, 0xb0, 0xff, 0xe0, 0x01, + 0xe9, 0xff, 0x80, 0x68, 0x21, 0xff, 0xa9, 0x06, 0x02, 0x01, 0xf2, 0xbf, 0x40, 0x12, 0x44, 0x1f, + 0x2b, 0x82, 0xa1, 0x7d, 0x11, 0xf8, 0x05, 0xf8, 0x1e, 0x74, 0x7f, 0x80, 0xc0, 0x75, 0x50, 0xa8, + 0x04, 0x83, 0xf0, 0x0f, 0x1b, 0xe1, 0x48, 0x6f, 0xfb, 0x78, 0x38, 0x3c, 0x40, 0x09, 0xfc, 0x81, + 0x83, 0xcb, 0xfb, 0xbf, 0xbf, 0xcb, 0xbe, 0x1a, 0x8d, 0x56, 0xaa, 0x55, 0x80, 0x85, 0x18, 0xc4, + 0x5e, 0xb3, 0xce, 0x00, 0x7d, 0x7b, 0x80, 0x21, 0xdf, 0xcf, 0xef, 0xee, 0x72, 0x2f, 0x8e, 0xaa, + 0x01, 0xa8, 0xd1, 0x62, 0xa7, 0xfa, 0x0c, 0x06, 0x7d, 0x82, 0x33, 0x8f, 0x3d, 0xfe, 0x04, 0x18, + 0x15, 0x68, 0xc0, 0x1e, 0xa2, 0x02, 0x1f, 0x0f, 0xff, 0xbf, 0x3f, 0xe3, 0xcf, 0xc7, 0x63, 0xca, + 0xd5, 0xe7, 0xb5, 0x58, 0xa8, 0x3e, 0xa3, 0x80, 0x8e, 0x29, 0xfe, 0x3c, 0x2e, 0x30, 0x0b, 0x85, + 0x56, 0xea, 0x73, 0x11, 0x10, 0xd8, 0x01, 0xe5, 0xcf, 0x9f, 0xcf, 0xf5, 0x50, 0x2d, 0x61, 0x80, + 0x75, 0x38, 0xa2, 0x28, 0xda, 0x09, 0x63, 0x9f, 0x1f, 0x0c, 0xf8, 0x3e, 0x35, 0x6a, 0xad, 0x54, + 0x3a, 0x20, 0x01, 0xba, 0x7f, 0xf7, 0xf9, 0xff, 0xbc, 0xfe, 0x7c, 0x9c, 0x1d, 0x58, 0x7c, 0x74, + 0xd0, 0xad, 0x16, 0xab, 0x71, 0xbf, 0xef, 0xfb, 0xc1, 0xe3, 0x0c, 0x1d, 0x08, 0x78, 0x6a, 0xb0, + 0xd0, 0xf9, 0x66, 0x3f, 0xbf, 0xff, 0x3f, 0x9c, 0x9c, 0x3a, 0x8e, 0x50, 0x78, 0x75, 0x59, 0xa8, + 0x50, 0x0c, 0x7c, 0x9f, 0xff, 0xcf, 0x9f, 0xe3, 0x07, 0x0b, 0x8d, 0x00, 0x3e, 0x51, 0x50, 0xf9, + 0x7c, 0x3e, 0x7f, 0x99, 0x3f, 0xe2, 0x0f, 0x2a, 0x5f, 0x10, 0x00, 0xb1, 0x4c, 0x31, 0xf8, 0x7c, + 0x77, 0x10, 0x3f, 0xe4, 0x3c, 0x2e, 0x36, 0x00, 0xf9, 0x4d, 0x43, 0xe5, 0xe0, 0x9c, 0xfb, 0xa0, + 0x7e, 0x88, 0x78, 0x68, 0x76, 0x41, 0xe2, 0xb5, 0x17, 0x86, 0x05, 0xf8, 0xcc, 0x7f, 0xe0, 0x3f, + 0xc8, 0x30, 0x5c, 0x6a, 0x01, 0xf2, 0xaa, 0x87, 0xcb, 0xf1, 0xc2, 0xff, 0xc0, 0x00, 0x22, 0x3e, + 0x50, 0x01, 0x42, 0xaf, 0x1d, 0x09, 0xfe, 0x00, 0x1e, 0x20, 0x01, 0xac, 0x07, 0x8b, 0xaa, 0x07, + 0x29, 0xe7, 0x0a, 0x1e, 0x0f, 0x20, 0xfc, 0x4a, 0xa7, 0x9e, 0x85, 0xa8, 0xf0, 0xc2, 0xe5, 0x54, + 0x1f, 0x9c, 0x04, 0x3e, 0x5e, 0x00, 0x18, 0xf1, 0xe0, 0x3a, 0x06, 0xf1, 0xb8, 0x08, 0x7e, 0x33, + 0x8a, 0x04, 0x02, 0x51, 0xe3, 0x4f, 0x90, 0x7d, 0xc0, 0x07, 0xfa, 0x80, 0x68, 0xe1, 0x5e, 0xc0, + 0xf1, 0x6e, 0x83, 0xe4, 0x0e, 0x28, 0x57, 0xe3, 0xce, 0x18, 0x1e, 0xa0, 0x78, 0xab, 0xa1, 0xf6, + 0xfc, 0x7f, 0xf0, 0x72, 0xa0, 0xfa, 0xca, 0xa5, 0x50, 0x08, 0x46, 0x01, 0x46, 0x3f, 0xf3, 0x18, + 0x87, 0xf3, 0xbf, 0x00, 0xd5, 0x7b, 0x37, 0xfa, 0xaf, 0x56, 0xff, 0x53, 0x82, 0x0f, 0x8c, 0x83, + 0x51, 0x02, 0xff, 0x5f, 0xc2, 0x63, 0xd4, 0xaf, 0xa7, 0x86, 0xbe, 0x1f, 0x6d, 0xf5, 0xff, 0x51, + 0x2a, 0xd6, 0x6b, 0xc7, 0xe3, 0xaf, 0xd4, 0xe0, 0x03, 0xe2, 0x06, 0x18, 0x17, 0xf2, 0xf6, 0x7f, + 0xd2, 0x78, 0xea, 0x21, 0x49, 0xf4, 0x80, 0xe0, 0x35, 0x40, 0x11, 0x0f, 0x97, 0xfc, 0x30, 0x21, + 0xfb, 0xbf, 0x43, 0xc3, 0xed, 0x81, 0x0e, 0x90, 0x7c, 0xa7, 0xd1, 0xf1, 0xf9, 0x5d, 0x6a, 0xa0, + 0x11, 0x0f, 0xb4, 0xf2, 0x7c, 0x57, 0x16, 0xfa, 0xe2, 0x80, 0xa7, 0x00, 0x9c, 0x07, 0xca, 0xac, + 0x3f, 0x35, 0x30, 0x80, 0x19, 0x6e, 0x3a, 0xef, 0x52, 0xf8, 0x75, 0x5a, 0xbd, 0x10, 0x80, 0x90, + 0x43, 0xe0, 0x98, 0x04, 0x45, 0x3a, 0x2b, 0xe1, 0xaa, 0xc1, 0x60, 0x16, 0x00, 0xe9, 0xfd, 0x2d, + 0x1a, 0xac, 0x56, 0xbb, 0x55, 0xfb, 0x01, 0x0e, 0x2f, 0x45, 0xb2, 0x0f, 0x8c, 0x2a, 0x01, 0xfc, + 0xec, 0x40, 0x6c, 0x01, 0xf1, 0xab, 0xb0, 0x46, 0x6b, 0x00, 0xee, 0x27, 0xd3, 0x51, 0x0e, 0xdc, + 0x07, 0x07, 0x56, 0x02, 0x1f, 0x5e, 0x03, 0x56, 0x1f, 0x1d, 0x04, 0x2b, 0xfd, 0x59, 0xfa, 0xd5, + 0xcb, 0x83, 0x57, 0x01, 0x95, 0xff, 0xaa, 0x1f, 0x3f, 0xfe, 0xdc, 0x2a, 0x21, 0xc1, 0xfd, 0x1e, + 0xce, 0xd5, 0x0a, 0x85, 0x10, 0x06, 0x7b, 0x53, 0x3e, 0x18, 0x01, 0x43, 0xe7, 0xc0, 0x7d, 0x90, + 0x7c, 0x40, 0x09, 0x2a, 0x94, 0x4a, 0x17, 0xe3, 0xe3, 0xb5, 0x87, 0xa0, 0xc2, 0x20, 0x01, 0xc0, + 0x78, 0x7b, 0xe1, 0xc5, 0x00, 0x16, 0x68, 0x42, 0x3a, 0xae, 0x17, 0xc7, 0x3e, 0x1f, 0x88, 0x68, + 0xf8, 0xfe, 0x10, 0x13, 0xb1, 0x00, 0x0c, 0x0c, 0x3a, 0x65, 0x48, 0x8f, 0xc7, 0xf0, 0xdd, 0x57, + 0x0c, 0x7f, 0x14, 0x22, 0x5f, 0x5d, 0x5f, 0xeb, 0x51, 0xaa, 0xd1, 0x07, 0xc7, 0xf8, +}; +const uint8_t _A_Levelup1_128x64_4[] = { + 0x01, 0x00, 0x7e, 0x02, 0xff, 0x80, 0x3c, 0x01, 0xf1, 0xff, 0xe0, 0x3f, 0x7d, 0xff, 0xd1, 0x0f, + 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x2d, 0xff, 0xee, 0x0f, 0xef, 0x47, 0xff, 0xfc, 0x06, 0x2d, + 0xf8, 0x3d, 0xe0, 0x3e, 0x49, 0x3f, 0xc0, 0xfc, 0x7f, 0x8f, 0xfc, 0xed, 0x02, 0x37, 0xff, 0xc5, + 0xbe, 0x01, 0xf0, 0xaf, 0xe0, 0xc1, 0xe7, 0xe0, 0xff, 0xc0, 0xc2, 0xe3, 0x1f, 0xf8, 0x4f, 0xe0, + 0x17, 0x0b, 0xf5, 0x81, 0x34, 0x40, 0xf1, 0xb8, 0xc1, 0x38, 0x1f, 0xd8, 0x3f, 0xe2, 0x73, 0x00, + 0xf7, 0xc7, 0xe4, 0x02, 0xb1, 0xff, 0xcf, 0xf0, 0x02, 0x81, 0xc0, 0x77, 0xe0, 0x37, 0xf8, 0x47, + 0x82, 0xff, 0x0e, 0x6e, 0x1c, 0x1d, 0xc4, 0x3e, 0x08, 0x0d, 0xfb, 0x80, 0x2e, 0x1f, 0x00, 0x08, + 0x60, 0x06, 0x02, 0xc3, 0xc0, 0x41, 0x23, 0x81, 0x04, 0x89, 0x38, 0x6c, 0x11, 0xfc, 0x04, 0x3c, + 0x1e, 0x3f, 0x78, 0x3c, 0x0e, 0x01, 0xc0, 0x83, 0xff, 0xa0, 0x60, 0x52, 0x8c, 0x88, 0x45, 0x00, + 0x63, 0x02, 0xe2, 0x6a, 0xe3, 0xc0, 0x41, 0x00, 0x09, 0xf8, 0xd2, 0x63, 0x4f, 0x04, 0x1d, 0xfc, + 0x1e, 0x65, 0xd2, 0x01, 0x09, 0xc8, 0x60, 0xd0, 0x0d, 0x66, 0xab, 0x54, 0x45, 0x10, 0x00, 0xfb, + 0x3f, 0xff, 0x3f, 0x8f, 0xcf, 0xd9, 0xf9, 0x87, 0xd4, 0x06, 0xc5, 0x03, 0x8a, 0x03, 0xc7, 0xf9, + 0xcf, 0xf3, 0xdf, 0xfc, 0xfc, 0x4f, 0xfc, 0x2e, 0x01, 0xab, 0x0f, 0x88, 0x90, 0x45, 0xe5, 0x87, + 0x1f, 0x94, 0x02, 0x9f, 0x08, 0xca, 0x01, 0x8a, 0x9f, 0x15, 0x07, 0x8d, 0xe3, 0x80, 0x0f, 0x30, + 0xc8, 0xf0, 0x03, 0xc3, 0xaa, 0x8d, 0x14, 0x13, 0x9f, 0xfb, 0x07, 0x8c, 0x02, 0x21, 0xd0, 0x0b, + 0x15, 0x7e, 0x2a, 0x51, 0x1b, 0x07, 0xfb, 0xcd, 0xff, 0xe1, 0x9f, 0x8b, 0x40, 0x1f, 0x29, 0x50, + 0x78, 0xa7, 0x93, 0x9c, 0x87, 0xfe, 0x03, 0x1f, 0x5f, 0x90, 0x7c, 0xa7, 0xf5, 0x5a, 0x84, 0x07, + 0x7c, 0x95, 0x1f, 0xbb, 0x48, 0x41, 0xe3, 0xb0, 0x0f, 0x95, 0xa8, 0x3e, 0x51, 0xe7, 0x00, 0xdf, + 0xe6, 0x0a, 0xf1, 0x82, 0x29, 0xce, 0xff, 0x55, 0xaa, 0xc0, 0x60, 0x90, 0x0c, 0x04, 0x7f, 0xfd, + 0xfd, 0x20, 0x0e, 0xa0, 0x3e, 0x5a, 0xa2, 0xf9, 0x9a, 0x47, 0x8e, 0x19, 0x14, 0xf0, 0xfe, 0x58, + 0xe7, 0x54, 0xaa, 0x84, 0x13, 0xdf, 0xfb, 0x12, 0x88, 0x7e, 0x4b, 0x43, 0xe3, 0xb7, 0xc0, 0x2a, + 0x9f, 0xd0, 0xf9, 0x8f, 0xc5, 0xe4, 0x9c, 0x38, 0x18, 0x10, 0x5b, 0xc4, 0xe0, 0x50, 0x79, 0x60, + 0x40, 0x63, 0x40, 0x0f, 0xbd, 0x50, 0x26, 0xaf, 0x71, 0x0f, 0x16, 0xe8, 0x16, 0xd1, 0xfb, 0x85, + 0xc2, 0x00, 0x1f, 0x50, 0x80, 0xd5, 0x22, 0x20, 0xf1, 0xff, 0x7e, 0x3f, 0x01, 0xfb, 0xec, 0x7f, + 0xef, 0x12, 0x08, 0x07, 0xf0, 0x3e, 0x71, 0x58, 0x24, 0x16, 0x28, 0x1e, 0x20, 0x40, 0xf8, 0xe0, + 0x3e, 0xcd, 0xfe, 0xab, 0xd5, 0xbf, 0x0f, 0xad, 0x6d, 0x3e, 0x41, 0xf5, 0xa8, 0xd5, 0x60, 0xa0, + 0x11, 0x67, 0x8e, 0xfa, 0xfe, 0x0f, 0x1d, 0x66, 0xbc, 0x7e, 0x3a, 0xf0, 0xfa, 0xff, 0xe3, 0xe0, + 0x11, 0x0f, 0xad, 0x56, 0x8b, 0x41, 0xaa, 0xc5, 0x60, 0x1f, 0xb8, 0x4e, 0x5c, 0x1e, 0x29, 0xf9, + 0xe0, 0x7f, 0xe1, 0xb1, 0xf8, 0x7a, 0xfd, 0xc3, 0xe3, 0x05, 0xb0, 0x43, 0xf8, 0x17, 0xf6, 0xf8, + 0x87, 0xe6, 0x61, 0x04, 0xf0, 0x46, 0x20, 0x15, 0x50, 0xfc, 0x04, 0xca, 0xe4, 0x15, 0x26, 0xfd, + 0x40, 0x22, 0x00, 0x21, 0xa8, 0xa5, 0x08, 0x00, 0x35, 0x3a, 0xad, 0x50, 0xbc, 0x20, 0x01, 0xb0, + 0x00, 0xcb, 0x5d, 0xea, 0x5f, 0x0e, 0xaa, 0x06, 0x1f, 0x5f, 0xc4, 0xc2, 0x01, 0x15, 0x0f, 0x1f, + 0xfe, 0xaf, 0x55, 0xb2, 0x9f, 0xc8, 0x1e, 0x36, 0x88, 0x06, 0x03, 0x5d, 0xaa, 0xfd, 0x80, 0x86, + 0x17, 0x00, 0xd8, 0x07, 0xcf, 0xdd, 0xf9, 0xa8, 0xf9, 0x43, 0xe9, 0x3f, 0xaa, 0xff, 0x08, 0x02, + 0x61, 0x1f, 0xf6, 0xae, 0x1d, 0xb8, 0x0e, 0x0e, 0xac, 0x04, 0x3e, 0xbc, 0x06, 0xac, 0x3c, 0x58, + 0x0f, 0xff, 0xec, 0x3a, 0x80, 0x04, 0xaa, 0x20, 0xc7, 0xc1, 0x4f, 0xd9, 0x9e, 0x84, 0x38, 0x3f, + 0xa4, 0x09, 0xda, 0x87, 0xe7, 0x08, 0xfb, 0x87, 0xa2, 0xff, 0x55, 0xa8, 0x18, 0x0b, 0x4e, 0x1f, + 0x30, 0x02, 0x4a, 0x90, 0x09, 0xef, 0x0f, 0xc1, 0x84, 0x60, 0xb4, 0x16, 0xe0, 0xf7, 0x43, 0x8a, + 0x00, 0x28, 0xd0, 0x7d, 0x0c, 0x22, 0x00, 0x7a, 0x79, 0x78, 0x55, 0x62, 0x10, 0x1d, 0x58, 0x75, + 0x40, 0xc3, 0x06, 0x60, 0xab, 0x75, 0x1c, 0x2b, 0x55, 0xa0, 0xb0, 0x4c, 0xbe, 0xba, 0xbf, 0xd0, + 0x79, 0x40, +}; +const uint8_t _A_Levelup1_128x64_5[] = { + 0x01, 0x00, 0x79, 0x02, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1f, 0xff, 0xfb, 0xcf, 0xc0, + 0xc9, 0xf0, 0xf1, 0x84, 0x1e, 0xff, 0xfe, 0x80, 0xff, 0x3f, 0xb7, 0xbf, 0xe7, 0x3c, 0x1c, 0xdb, + 0xff, 0xfc, 0xff, 0xfc, 0x1b, 0x80, 0x02, 0x73, 0xf5, 0xf9, 0xf8, 0xff, 0xff, 0x8f, 0x9f, 0xfc, + 0x0b, 0xde, 0xe5, 0xdd, 0xba, 0xf3, 0xbe, 0x3c, 0x78, 0x08, 0xf5, 0xe6, 0x3a, 0xcd, 0x56, 0xaa, + 0xc3, 0xf0, 0xa7, 0xe0, 0x3f, 0xff, 0xe1, 0xf2, 0x5f, 0x20, 0x15, 0x6a, 0xad, 0x57, 0x07, 0xf3, + 0x87, 0xf8, 0x3f, 0xfe, 0x11, 0x02, 0x4d, 0xe0, 0x1a, 0xbd, 0x76, 0xaf, 0x0d, 0xff, 0x80, 0x7c, + 0x3f, 0xbc, 0x70, 0x7a, 0x4f, 0xf0, 0x41, 0xe1, 0xaa, 0xfb, 0x67, 0xf0, 0x02, 0x70, 0x9c, 0x4c, + 0x7c, 0x33, 0xfe, 0x3e, 0xa8, 0x3e, 0x31, 0xa8, 0x78, 0x3c, 0x89, 0xc9, 0xd0, 0xff, 0xd1, 0xea, + 0xb5, 0x7a, 0xbc, 0x56, 0x01, 0x00, 0xfe, 0x03, 0xd4, 0x2c, 0x3a, 0xf2, 0xf0, 0xea, 0xa5, 0x40, + 0xf1, 0x9c, 0x1f, 0xdb, 0x1f, 0x67, 0x0f, 0x94, 0xd6, 0x01, 0x04, 0x80, 0x18, 0x07, 0x7f, 0xef, + 0x07, 0x94, 0x3f, 0x64, 0x1f, 0x2b, 0x50, 0x7c, 0x46, 0x25, 0xfd, 0xef, 0xb7, 0x47, 0x3f, 0x0c, + 0xa8, 0x07, 0xc8, 0xc2, 0x30, 0x10, 0x78, 0xfc, 0x08, 0x09, 0x18, 0xd4, 0x6a, 0x90, 0x03, 0xaa, + 0x06, 0x8c, 0x70, 0x60, 0x47, 0xd2, 0xff, 0xcc, 0xfb, 0x01, 0xd2, 0xef, 0xd5, 0x20, 0x37, 0xf0, + 0x78, 0xfe, 0xc2, 0x05, 0xc7, 0x83, 0xfa, 0x35, 0x46, 0x21, 0xdd, 0x40, 0x30, 0x10, 0xfc, 0x63, + 0xd4, 0x5f, 0x03, 0xef, 0x07, 0x8d, 0xd7, 0xab, 0x55, 0xae, 0xdd, 0x6a, 0xb8, 0x58, 0x7e, 0xff, + 0xce, 0x1d, 0x12, 0x10, 0xfc, 0xe0, 0xfa, 0xcf, 0xd6, 0xa8, 0x02, 0x24, 0xe1, 0x81, 0xb7, 0x84, + 0xdc, 0x57, 0xf9, 0x7c, 0x02, 0xab, 0x75, 0x00, 0x0d, 0x56, 0x82, 0x10, 0x10, 0x78, 0xc3, 0xde, + 0x25, 0x06, 0x3b, 0x0e, 0xeb, 0x55, 0xea, 0xfd, 0x1b, 0x8a, 0xff, 0x7f, 0xbf, 0x2c, 0xc7, 0xe7, + 0x0e, 0x80, 0x75, 0x2a, 0xb7, 0x7a, 0xbd, 0x76, 0x8b, 0x05, 0x80, 0x06, 0xc6, 0xf8, 0xb2, 0x1b, + 0x1a, 0x58, 0x83, 0xec, 0x6e, 0x41, 0x70, 0xd5, 0x0b, 0xc2, 0x00, 0x10, 0xf9, 0x08, 0x04, 0x7a, + 0x5f, 0xf8, 0x60, 0x7c, 0xb7, 0xe1, 0xf1, 0xff, 0x87, 0xca, 0x01, 0xfd, 0x07, 0x96, 0x26, 0x07, + 0x40, 0xfa, 0xb1, 0x47, 0xbf, 0x7f, 0xa2, 0xb0, 0x0b, 0xf1, 0x7f, 0x10, 0x8f, 0x82, 0x0f, 0x00, + 0xd6, 0x7b, 0xcf, 0xe3, 0xaf, 0x0f, 0x96, 0x03, 0xff, 0x39, 0xff, 0x63, 0x7f, 0xf1, 0xf3, 0xf9, + 0x00, 0x4a, 0x82, 0x01, 0x1c, 0x07, 0x7c, 0xc4, 0x38, 0x71, 0x88, 0xc0, 0x75, 0x83, 0xf2, 0x0f, + 0x9c, 0x02, 0x0c, 0x21, 0x17, 0x01, 0x41, 0xea, 0x85, 0xf2, 0x00, 0x12, 0x84, 0xb0, 0x0d, 0x11, + 0xff, 0xcf, 0xa0, 0xba, 0xcd, 0x50, 0x00, 0x83, 0xe7, 0x00, 0xe0, 0x7c, 0x57, 0xe3, 0x78, 0x9c, + 0x55, 0x07, 0xe7, 0x55, 0x8a, 0xd0, 0x02, 0x43, 0xe0, 0xfe, 0x7d, 0xff, 0xfe, 0x9b, 0xc0, 0x7e, + 0x60, 0x13, 0x07, 0x01, 0x5e, 0x2a, 0xc3, 0xd3, 0x43, 0x0a, 0x04, 0x42, 0x05, 0x8c, 0xc1, 0xc7, + 0xfe, 0x1a, 0xef, 0x56, 0xa9, 0x82, 0x30, 0x30, 0xfa, 0x08, 0x4a, 0x1d, 0x40, 0xaa, 0xb0, 0x06, + 0xbb, 0x55, 0x82, 0xd4, 0x2c, 0xa4, 0x21, 0x80, 0x7c, 0x20, 0x76, 0x83, 0x20, 0xeb, 0xb5, 0x5f, + 0xb0, 0x10, 0xc2, 0xe0, 0x1b, 0x00, 0xe9, 0x2c, 0x47, 0xb1, 0x01, 0x0b, 0x8f, 0x56, 0xaf, 0x5f, + 0xaa, 0xcd, 0x6a, 0x9d, 0xca, 0xa3, 0x84, 0xf5, 0x10, 0xed, 0xc0, 0x70, 0x75, 0x60, 0x21, 0xf5, + 0xe0, 0x35, 0x6c, 0x13, 0xbf, 0x56, 0xfe, 0xb5, 0x72, 0xe0, 0xd5, 0xc0, 0x65, 0x7f, 0xea, 0xd1, + 0x20, 0x14, 0x0f, 0x84, 0xff, 0x6e, 0x7a, 0x10, 0xe0, 0xfe, 0x90, 0x27, 0x6a, 0x1f, 0x97, 0x82, + 0x7e, 0x19, 0x20, 0x03, 0x8f, 0x86, 0x01, 0xe7, 0xbf, 0xeb, 0x43, 0xe6, 0x00, 0x49, 0x50, 0x38, + 0x7f, 0x9c, 0xff, 0xfd, 0xbc, 0x3d, 0x13, 0x5a, 0xa9, 0x38, 0x78, 0xf9, 0xce, 0xf0, 0x71, 0x40, + 0x05, 0x1a, 0x2f, 0x0f, 0x7f, 0xff, 0xf9, 0xd8, 0x07, 0xe2, 0x9b, 0x50, 0x80, 0x33, 0x47, 0xc7, + 0x00, 0xd5, 0x87, 0x54, 0x0c, 0x30, 0x61, 0xf8, 0xdf, 0xfe, 0xc0, 0xf1, 0x6e, 0xa3, 0x85, 0x0c, + 0x03, 0x1c, 0x10, 0x0a, 0xc8, 0x4b, 0xeb, 0xab, 0xfd, 0x37, 0x4e, 0x77, 0xfe, +}; +const uint8_t _A_Levelup1_128x64_6[] = { + 0x01, 0x00, 0x38, 0x02, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, 0x5f, 0x78, 0x37, + 0x20, 0x3f, 0xc0, 0x7e, 0x4f, 0xff, 0xde, 0x3e, 0x38, 0xae, 0xf9, 0xf0, 0x5c, 0xe8, 0x7e, 0xf8, + 0xf3, 0x3c, 0x45, 0x83, 0xdc, 0x1f, 0xb8, 0x6e, 0x23, 0x01, 0xfd, 0x1f, 0xdc, 0x0a, 0x09, 0x01, + 0xfd, 0x03, 0xaa, 0xff, 0x01, 0x07, 0x8f, 0xd0, 0x1f, 0x5b, 0xf8, 0x00, 0x3c, 0x7d, 0x00, 0xfa, + 0xaf, 0x83, 0xcb, 0xa0, 0x0e, 0x9f, 0xb8, 0x3c, 0x60, 0x50, 0x20, 0x7d, 0xcb, 0xc1, 0xe5, 0xa0, + 0xdb, 0x83, 0xe3, 0xc6, 0x0f, 0x4d, 0xff, 0xdc, 0x3f, 0x11, 0x70, 0x79, 0x50, 0x23, 0xe0, 0xe7, + 0xfe, 0x83, 0xd7, 0x7e, 0x2f, 0x8f, 0xe4, 0x2e, 0x00, 0x80, 0x1c, 0x06, 0xf9, 0x1b, 0x1f, 0xcc, + 0x5c, 0x1c, 0x1e, 0x38, 0xfd, 0xf6, 0x0f, 0xbe, 0xb3, 0x5b, 0xfd, 0xf2, 0x97, 0x08, 0xff, 0xe1, + 0xf2, 0x1e, 0xec, 0x38, 0xfd, 0xff, 0x12, 0x5c, 0x84, 0x06, 0x99, 0x20, 0xfa, 0xff, 0x81, 0xed, + 0xfb, 0xfd, 0xc9, 0x6a, 0x10, 0x0a, 0x84, 0x80, 0x58, 0x9e, 0xf3, 0x7a, 0x55, 0x66, 0x12, 0x0b, + 0x05, 0x40, 0x1e, 0x5f, 0x11, 0x0b, 0x75, 0x66, 0x12, 0x2b, 0x15, 0x63, 0x7a, 0x07, 0x1f, 0x80, + 0xf4, 0xca, 0x05, 0xd5, 0x88, 0x48, 0xa8, 0x55, 0x80, 0x40, 0xdf, 0x3e, 0x1f, 0x30, 0x60, 0xff, + 0xb0, 0xfe, 0x3f, 0xf0, 0x3d, 0x6e, 0xf1, 0xef, 0xc7, 0xec, 0x40, 0x58, 0x7e, 0x1f, 0xbf, 0xab, + 0x11, 0x80, 0x50, 0x2b, 0x81, 0xf1, 0x87, 0xff, 0x0c, 0x49, 0x07, 0xc1, 0xf5, 0x63, 0xf0, 0x0e, + 0x05, 0x7c, 0x7e, 0x30, 0x70, 0x71, 0x35, 0x0a, 0x7f, 0x02, 0xd2, 0xe7, 0xff, 0xfc, 0xf9, 0x78, + 0x7c, 0x7c, 0x7f, 0xf8, 0xf8, 0x44, 0xaf, 0xb7, 0xeb, 0x14, 0x92, 0x9f, 0x00, 0xe8, 0x49, 0xf9, + 0xb3, 0xdf, 0x01, 0x64, 0x40, 0x42, 0x1e, 0x1f, 0xe5, 0xda, 0x89, 0x30, 0x80, 0x58, 0x22, 0x1f, + 0x58, 0xff, 0xe2, 0xf7, 0xfc, 0xf0, 0x83, 0xca, 0x01, 0x78, 0xc2, 0x0f, 0xca, 0x1f, 0xfd, 0x87, + 0xff, 0xaf, 0xbf, 0xc1, 0x90, 0xff, 0xe3, 0xb0, 0x0f, 0x84, 0x12, 0x20, 0x5f, 0x18, 0x07, 0xf5, + 0x56, 0x5f, 0xcf, 0xfe, 0xff, 0xbf, 0x18, 0x07, 0x94, 0x82, 0x00, 0x01, 0x1f, 0xe8, 0x30, 0x7f, + 0xbe, 0xcf, 0xf5, 0xef, 0xfd, 0x38, 0x83, 0x70, 0xa0, 0xa0, 0xf3, 0x80, 0xfe, 0x38, 0x56, 0xf0, + 0x90, 0xff, 0xff, 0x31, 0x08, 0x7c, 0x10, 0x3e, 0x80, 0xf1, 0xa1, 0x9b, 0x87, 0xef, 0xfd, 0xf9, + 0xc0, 0x20, 0xf1, 0x88, 0x30, 0xf4, 0x48, 0x23, 0x03, 0xbf, 0x79, 0xe0, 0x7f, 0xe2, 0x18, 0xfc, + 0xf8, 0x10, 0x3f, 0x80, 0x3d, 0x67, 0x90, 0x18, 0x04, 0x73, 0xf8, 0x10, 0x1d, 0x9c, 0x0e, 0xe1, + 0x05, 0xe0, 0x40, 0x0a, 0xa3, 0x05, 0x41, 0x8c, 0x00, 0x04, 0x2f, 0x21, 0xa5, 0xc3, 0xb2, 0x10, + 0x4c, 0x07, 0xe6, 0x19, 0x00, 0x84, 0xc0, 0x32, 0x00, 0xf1, 0x80, 0x78, 0x3d, 0xf8, 0xb8, 0x0d, + 0x80, 0x1e, 0x30, 0x78, 0x7c, 0x02, 0x63, 0x00, 0xc6, 0x29, 0x87, 0xe0, 0xb0, 0x18, 0x5c, 0x3a, + 0x31, 0x04, 0x2e, 0x08, 0x08, 0x78, 0x38, 0x0c, 0x1e, 0xe2, 0x01, 0xfe, 0xf8, 0x83, 0xc7, 0xe1, + 0x07, 0x06, 0x0c, 0x1a, 0x06, 0x12, 0x1b, 0xfe, 0x03, 0x01, 0x0f, 0xf7, 0x7f, 0xfd, 0x91, 0x71, + 0xa0, 0x83, 0xc7, 0xf0, 0x3a, 0x25, 0x98, 0xf0, 0x21, 0x69, 0x01, 0xe0, 0x83, 0xd0, 0x00, 0x80, + 0xf1, 0x88, 0xf6, 0x3f, 0x18, 0x79, 0x78, 0x3e, 0x14, 0x62, 0x6e, 0x1b, 0xc4, 0x7e, 0x4e, 0x0f, + 0xaf, 0x85, 0xc7, 0xf1, 0x3f, 0xa1, 0x83, 0x14, 0x00, 0x4a, 0x31, 0xf0, 0x40, 0xc1, 0xe3, 0x87, + 0x07, 0xc7, 0x20, 0x04, 0x30, 0xee, 0x7c, 0xbe, 0x06, 0xb6, 0x10, 0x02, 0x01, 0x87, 0xe9, 0xc3, + 0x87, 0xfe, 0x23, 0x77, 0xc8, 0x2c, 0x18, 0x7e, 0xa0, 0xf1, 0xfd, 0xf9, 0xe3, 0xf7, 0x0b, 0xdf, + 0xf0, 0xf6, 0x40, 0xf5, 0xfc, 0x5c, 0x27, 0x93, 0xc5, 0x70, 0xff, 0xc0, +}; +const uint8_t _A_Levelup1_128x64_7[] = { + 0x01, 0x00, 0xd0, 0x01, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x05, 0xfd, 0x83, 0xfb, 0xbe, 0x20, 0xf0, 0xff, 0x83, 0x72, + 0x01, 0xfe, 0x07, 0xdf, 0xf6, 0x3f, 0xff, 0xf8, 0x83, 0xf3, 0xcf, 0x03, 0xe7, 0x40, 0xff, 0xc0, + 0xf9, 0xfc, 0x46, 0x61, 0x93, 0x00, 0xfe, 0x41, 0xa2, 0x1c, 0x0e, 0xf0, 0x3e, 0xaf, 0xb0, 0x19, + 0x06, 0x03, 0xba, 0x0f, 0xad, 0xfc, 0x02, 0x81, 0x00, 0xed, 0x83, 0xea, 0xbe, 0x0f, 0x2f, 0xa8, + 0x3e, 0xa5, 0xf0, 0x0c, 0x04, 0x03, 0xd4, 0x0f, 0xdf, 0xc7, 0xad, 0xb5, 0x51, 0x70, 0x78, 0xc1, + 0xfa, 0xdb, 0xf0, 0x84, 0xc5, 0xff, 0xdc, 0x08, 0x07, 0x60, 0x3f, 0x50, 0xb8, 0x0c, 0x86, 0x81, + 0xb0, 0x0f, 0xcf, 0xef, 0x17, 0x7c, 0x89, 0x50, 0x37, 0x00, 0xfa, 0xa5, 0xe0, 0xa1, 0x98, 0x20, + 0x7d, 0x78, 0x28, 0xb1, 0xd8, 0x2e, 0x0a, 0xa0, 0xe3, 0x10, 0xfc, 0x49, 0x63, 0xf0, 0x9c, 0x25, + 0xc2, 0x17, 0xfa, 0x07, 0xc6, 0x97, 0x3d, 0x0a, 0x82, 0x5f, 0xc3, 0xf3, 0xff, 0xd2, 0xff, 0xa1, + 0x5c, 0x0b, 0x81, 0x3e, 0xb4, 0x40, 0xf1, 0x98, 0x78, 0x37, 0xc1, 0xf9, 0x07, 0x94, 0x23, 0x81, + 0xbe, 0xff, 0x03, 0xe2, 0x85, 0xff, 0x85, 0x41, 0xb0, 0x00, 0xe1, 0xfe, 0x83, 0x56, 0x7e, 0x0f, + 0x1b, 0xef, 0x36, 0x03, 0x9c, 0x17, 0xc5, 0xff, 0xdd, 0x82, 0xfd, 0x4f, 0xe0, 0x1a, 0x3e, 0xf0, + 0xfe, 0x70, 0x51, 0xc8, 0x07, 0x03, 0x80, 0x3e, 0x9f, 0xfc, 0x0a, 0x17, 0x00, 0x90, 0x70, 0x3f, + 0xbf, 0x70, 0x7c, 0xc1, 0x20, 0x11, 0x0a, 0x01, 0x34, 0x7d, 0xe3, 0xe5, 0x03, 0x88, 0x3c, 0xa1, + 0x00, 0xf2, 0xf1, 0x87, 0xe6, 0x03, 0x06, 0x90, 0x03, 0xc7, 0xf7, 0xe7, 0x07, 0xbc, 0x03, 0x0d, + 0x01, 0x07, 0x90, 0x81, 0x7f, 0x00, 0xf3, 0xbc, 0x10, 0x04, 0x1e, 0x5f, 0x10, 0x79, 0xfc, 0x11, + 0xe2, 0x0f, 0x10, 0x00, 0xc2, 0x01, 0xe3, 0x7f, 0x50, 0x27, 0x82, 0x7f, 0xdc, 0x07, 0x9c, 0x10, + 0x18, 0x84, 0x12, 0x07, 0x8f, 0xf0, 0x1e, 0x70, 0x2f, 0xec, 0xfd, 0x22, 0x5e, 0x00, 0x10, 0xf0, + 0x7f, 0x82, 0xf4, 0x80, 0xef, 0x63, 0xe0, 0xf1, 0xdf, 0x94, 0x4a, 0x0f, 0xf0, 0x07, 0xa4, 0x03, + 0xfb, 0xc7, 0xff, 0xfd, 0xe1, 0xe2, 0x22, 0x8f, 0xf2, 0x2f, 0xf0, 0x10, 0x79, 0xfe, 0xc7, 0xc5, + 0x71, 0xe0, 0x28, 0x0c, 0x1e, 0xfc, 0xf0, 0x58, 0xe0, 0x4a, 0x87, 0xbe, 0x07, 0x1c, 0x03, 0xeb, + 0xff, 0xdb, 0x8f, 0x08, 0xc4, 0x7b, 0xf0, 0x7b, 0x52, 0xc1, 0x83, 0x0f, 0xff, 0xfc, 0xf3, 0x7f, + 0xfa, 0x78, 0x3d, 0x3d, 0x3f, 0xfa, 0x20, 0x3c, 0x71, 0xea, 0x31, 0xb2, 0xff, 0xa6, 0x60, 0xf4, + 0xc8, 0xc7, 0xe8, 0x7d, 0x01, 0xe9, 0xe1, 0x60, 0x30, 0xc0, 0x7a, 0x58, 0x7c, 0x14, 0x0f, 0x07, + 0x84, 0xc8, 0x50, 0x5f, 0xf4, 0x0c, 0x1e, 0x98, 0x18, 0xfc, 0x82, 0x00, 0x6e, 0x38, 0x28, 0x35, + 0x14, 0x05, 0x00, 0x90, 0x7c, 0x20, 0x7f, 0xe4, 0x80, 0xc2, 0xd7, 0x64, 0x0f, 0x58, 0x87, 0xc1, + 0x64, 0x39, 0xff, 0xfc, 0x20, 0x1f, 0x31, 0x8b, 0xf6, 0x78, 0xe0, 0x31, 0x70, 0x7c, 0x5a, 0x26, + 0x1f, 0x8c, 0x7a, 0x40, 0x41, 0xf3, 0xf4, 0xf0, 0x40, 0x70, 0x04, 0xf1, 0x97, 0x83, 0xe3, 0xf5, + 0xe1, 0x83, 0x98, 0x08, +}; +const uint8_t _A_Levelup1_128x64_8[] = { + 0x01, 0x00, 0xe6, 0x01, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x00, 0xbf, 0xb0, 0x7f, 0x88, 0x3c, 0x3f, 0xe1, 0x0f, 0x00, 0x5c, 0x3f, + 0xc0, 0x1f, 0xdf, 0x8c, 0x7c, 0x1f, 0x97, 0xfb, 0xf7, 0x83, 0xf8, 0x21, 0x10, 0x04, 0xe7, 0xf0, + 0x30, 0x7f, 0x98, 0x7e, 0xed, 0xf0, 0x08, 0x47, 0xb0, 0x1e, 0x7c, 0xf8, 0x4c, 0xcb, 0xe6, 0x0f, + 0x38, 0xbc, 0x02, 0x21, 0xd0, 0x03, 0x9e, 0x3f, 0x93, 0x33, 0x07, 0xa4, 0x2e, 0x01, 0x20, 0xdc, + 0x07, 0xcc, 0x1e, 0xf0, 0x50, 0x78, 0xd1, 0x80, 0xe7, 0x4f, 0x84, 0xc9, 0xbf, 0x20, 0x62, 0xff, + 0x40, 0xa0, 0x14, 0x0c, 0x0f, 0xfb, 0xde, 0x0f, 0x2e, 0x4c, 0x9c, 0x1e, 0xdc, 0x08, 0x07, 0xf4, + 0x1e, 0x5c, 0xfe, 0x44, 0x1f, 0xf0, 0x7e, 0x38, 0x0f, 0xfa, 0x81, 0x10, 0xfe, 0xf1, 0xc1, 0xe7, + 0xcf, 0xfe, 0x03, 0xca, 0xfd, 0x01, 0x80, 0xc8, 0x64, 0x1f, 0xa0, 0xf9, 0xc2, 0x79, 0xc0, 0xf2, + 0xbf, 0xff, 0x30, 0x17, 0xc8, 0x95, 0x00, 0x1e, 0x60, 0x06, 0xdf, 0x50, 0x30, 0x51, 0x4a, 0x0c, + 0x11, 0x3d, 0x3b, 0xe9, 0x04, 0x7e, 0x13, 0x42, 0xfe, 0x0e, 0x4c, 0x00, 0xb5, 0xfd, 0x04, 0xce, + 0x15, 0x04, 0x83, 0x24, 0x07, 0x9f, 0x83, 0xce, 0x48, 0x29, 0xff, 0x20, 0x78, 0xd7, 0xb4, 0x16, + 0x38, 0x94, 0x12, 0x03, 0xf4, 0x07, 0x8f, 0xcf, 0x39, 0x3c, 0x07, 0x98, 0x7c, 0x6e, 0x71, 0x44, + 0x00, 0xfa, 0x01, 0xe5, 0xc9, 0x07, 0xb7, 0xfe, 0x29, 0x20, 0x07, 0x9e, 0x0f, 0x92, 0x2f, 0x40, + 0x79, 0xc6, 0x30, 0x1e, 0xb0, 0xfa, 0x5b, 0xda, 0xe4, 0x0f, 0x38, 0x3d, 0x83, 0xd8, 0x0f, 0x2f, + 0x18, 0x3d, 0x64, 0x1f, 0xfe, 0x94, 0x02, 0x30, 0x3f, 0x30, 0x78, 0x9b, 0xd2, 0x81, 0xfe, 0x9d, + 0xc0, 0x20, 0x80, 0xf1, 0x87, 0xe0, 0xbe, 0xf2, 0x0a, 0x40, 0xfc, 0xf0, 0x11, 0xc8, 0x64, 0x02, + 0x04, 0x6f, 0x14, 0x7c, 0x40, 0x20, 0xb0, 0x88, 0x01, 0xfc, 0x81, 0xf3, 0x02, 0x80, 0x1f, 0xc7, + 0xf4, 0x0f, 0xc9, 0x80, 0x3f, 0x30, 0x10, 0x02, 0x00, 0xff, 0x41, 0xf5, 0x00, 0xc1, 0xc0, 0xf0, + 0x1f, 0xe0, 0x7d, 0xdf, 0x20, 0x14, 0x00, 0x41, 0x43, 0xc1, 0x03, 0x80, 0x07, 0xa8, 0x00, 0x54, + 0x62, 0x00, 0x1f, 0x98, 0x08, 0x6f, 0xe0, 0xf7, 0xe8, 0x02, 0x0a, 0x1a, 0x1f, 0x21, 0xb1, 0x03, + 0xd3, 0xb0, 0x0f, 0x28, 0x30, 0x1c, 0x8a, 0xc4, 0x0f, 0x4e, 0xa0, 0x3f, 0x85, 0xe7, 0x06, 0xce, + 0x62, 0x01, 0xe7, 0x4f, 0x07, 0xc6, 0x8b, 0x09, 0x01, 0x07, 0x8e, 0xfe, 0x5e, 0x5f, 0x70, 0x78, + 0xe0, 0xa0, 0x61, 0xf2, 0x07, 0xa5, 0x80, 0x1e, 0x98, 0x18, 0x38, 0x3c, 0xa2, 0xe0, 0xf4, 0xef, + 0xef, 0xc3, 0xee, 0x0f, 0x4d, 0x01, 0xe0, 0x7f, 0xc3, 0xf5, 0xff, 0xc2, 0xc1, 0xed, 0xfc, 0x2f, + 0xaf, 0x7e, 0x0a, 0x0f, 0x39, 0x00, 0x60, 0x8f, 0xe9, 0xf7, 0xf8, 0xbf, 0x44, 0x00, 0x54, 0x0f, + 0xd0, 0x40, 0x51, 0xeb, 0x78, 0x80, 0x83, 0xd3, 0xe8, 0x1f, 0x18, 0x72, 0xfd, 0x33, 0xe0, 0xe3, + 0x07, 0x8f, 0x20, 0x1e, 0x23, 0x22, 0x6f, 0x95, 0x9c, 0x1e, 0xb3, 0x0f, 0xff, 0xe3, 0xf6, 0x0c, + 0x13, 0xa0, 0xff, 0xe3, 0x34, 0x00, 0x7a, 0x70, 0x20, 0x1f, 0x78, 0x38, 0x18, 0x78, 0x1f, 0xf8, + 0x0c, 0x18, 0x1e, 0xb0, 0x0f, 0xff, 0xa1, 0x44, 0x40, 0x01, 0xff, 0x40, 0x41, 0xed, 0x00, 0xf4, + 0x7f, 0x7b, 0xe0, 0xf2, 0x2e, 0x88, 0x3e, 0x3c, 0x61, 0xf3, +}; +const uint8_t _A_Levelup1_128x64_9[] = { + 0x01, 0x00, 0xd6, 0x01, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x01, 0x3d, 0xe0, 0xff, 0x06, 0xe4, 0x0f, + 0xf0, 0x0f, 0xab, 0xf8, 0x04, 0x7f, 0x9c, 0x7c, 0x18, 0x7a, 0xf3, 0xf0, 0xf9, 0xc0, 0x7f, 0x2c, + 0xff, 0x0e, 0x07, 0xb8, 0x1f, 0x71, 0xb8, 0x04, 0x0f, 0xda, 0x09, 0x3e, 0x7c, 0x26, 0x65, 0xf3, + 0x07, 0x9c, 0x16, 0x01, 0x0c, 0xfd, 0x03, 0xcf, 0x1f, 0xc9, 0x99, 0x83, 0xd2, 0x05, 0x00, 0x88, + 0x71, 0x0e, 0x26, 0x0f, 0x59, 0x7d, 0xea, 0x03, 0x00, 0x90, 0x6f, 0x81, 0xe7, 0x4f, 0x84, 0xc9, + 0xbf, 0x21, 0xf2, 0xdf, 0xc0, 0x20, 0x14, 0x0a, 0x08, 0x3d, 0x39, 0x32, 0x70, 0x7b, 0x60, 0x30, + 0x1f, 0xee, 0x39, 0x3c, 0xb9, 0x10, 0x7f, 0xc0, 0x3c, 0xab, 0xf0, 0x09, 0xfd, 0x03, 0x21, 0xfe, + 0x80, 0x83, 0xcf, 0x9f, 0xfc, 0x2b, 0x15, 0x7f, 0x81, 0xc0, 0x90, 0x48, 0x3f, 0xa1, 0xf3, 0x84, + 0xf3, 0x81, 0xe5, 0x7f, 0x97, 0xc8, 0x23, 0xd1, 0x10, 0x78, 0xa8, 0x66, 0x5f, 0x90, 0xb0, 0xc2, + 0xa4, 0x3c, 0x20, 0x7d, 0x54, 0x33, 0xf0, 0x68, 0x14, 0xe8, 0x1f, 0x31, 0x7e, 0x46, 0xce, 0x09, + 0x02, 0xe3, 0x7f, 0x87, 0xc3, 0x83, 0xce, 0x48, 0x29, 0xff, 0x20, 0x79, 0x7a, 0x2c, 0x70, 0x5c, + 0x14, 0x07, 0xfa, 0x0f, 0x1f, 0x9e, 0x72, 0x78, 0x0f, 0x5b, 0x9c, 0x16, 0x81, 0x00, 0x60, 0x88, + 0x3c, 0x79, 0x20, 0xf6, 0xbf, 0xc1, 0x66, 0x00, 0xf3, 0xc1, 0xf2, 0x45, 0xec, 0x0f, 0x18, 0x67, + 0x03, 0xf6, 0x1f, 0x4b, 0x78, 0x18, 0x7f, 0xf0, 0xbe, 0x47, 0xfe, 0x09, 0x03, 0xf8, 0x03, 0xcf, + 0xc6, 0x0f, 0x5e, 0x87, 0xff, 0x85, 0x00, 0xfc, 0x5f, 0x41, 0xe8, 0x6f, 0x38, 0x5e, 0x42, 0x1f, + 0x3f, 0x80, 0x7f, 0x27, 0xdf, 0xe8, 0x7e, 0x0b, 0xef, 0x20, 0xa4, 0x0f, 0xca, 0x17, 0x20, 0x81, + 0x41, 0x20, 0x17, 0xfb, 0xfa, 0x3e, 0x21, 0x7c, 0x08, 0x0b, 0xc0, 0x77, 0xe0, 0xe8, 0x07, 0x8c, + 0x00, 0x1e, 0x3e, 0x0e, 0xf8, 0x3a, 0x3e, 0xe0, 0xf3, 0xfc, 0x2f, 0xa4, 0x1e, 0x49, 0xd1, 0xe7, + 0x40, 0xd7, 0xe2, 0x00, 0x6f, 0x18, 0x3c, 0x70, 0x1c, 0x18, 0x1f, 0xfd, 0x7e, 0x21, 0xf9, 0x80, + 0x7f, 0xa0, 0x28, 0xf2, 0xff, 0xc3, 0xc1, 0x03, 0x80, 0x07, 0xa8, 0x40, 0x61, 0xeb, 0xf1, 0xff, + 0xfc, 0xc0, 0x42, 0x75, 0x30, 0x79, 0x80, 0x04, 0x1e, 0x50, 0xd0, 0xf9, 0x11, 0x88, 0x1e, 0xa2, + 0xf2, 0x83, 0x01, 0x88, 0x8c, 0x40, 0xf4, 0xe0, 0x7f, 0x01, 0xfb, 0x30, 0x1c, 0x14, 0x1b, 0x39, + 0x88, 0x07, 0xc7, 0x38, 0x1e, 0x7a, 0x2c, 0x24, 0x04, 0x1e, 0xdc, 0x01, 0xf1, 0x03, 0xcb, 0x05, + 0x03, 0x07, 0xb7, 0xf0, 0x1e, 0xb8, 0x18, 0x38, 0x3c, 0xa0, 0xa0, 0xf2, 0xfc, 0x07, 0xe8, 0x1e, + 0x7e, 0x0f, 0xa8, 0xfe, 0x41, 0xe7, 0x00, 0xfa, 0x17, 0xe6, 0x04, 0x0f, 0x3f, 0x60, 0x3c, 0xcf, + 0xea, 0x0f, 0xeb, 0xfc, 0x04, 0x1e, 0x7d, 0x80, 0x79, 0x43, 0x97, 0xe8, 0x0f, 0x91, 0x59, 0x37, + 0xcb, 0x7e, 0xce, 0x4d, 0x40, 0x3c, 0x8e, 0x65, 0xbf, 0x07, 0x9d, 0x00, 0x1e, 0xd0, 0x75, 0x39, + 0x01, 0x46, 0xbe, 0x0f, 0x4a, 0x40, 0x3c, 0x80, 0x0b, 0x2f, 0x80, 0x48, 0x01, 0xe5, 0x88, 0x24, + 0x08, 0x01, 0xa2, 0xe0, 0xf4, 0x84, 0x13, 0x08, 0x00, 0x80, +}; +const uint8_t _A_Levelup1_128x64_10[] = { + 0x01, 0x00, 0xde, 0x01, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x09, 0xef, 0x06, 0xe4, 0x0f, + 0xf0, 0x0f, 0xcb, 0xff, 0xf3, 0x8f, 0xc7, 0x07, 0xde, 0x7e, 0x1f, 0x38, 0x0f, 0xe5, 0x9f, 0xe1, + 0xc0, 0xf7, 0x03, 0xee, 0x37, 0x00, 0x81, 0xff, 0x4a, 0x27, 0xcf, 0x84, 0xcc, 0xbe, 0x63, 0xd2, + 0xff, 0xc1, 0x60, 0x10, 0xcf, 0xd0, 0x5c, 0xf1, 0xfc, 0x99, 0x98, 0x3d, 0x20, 0x50, 0x08, 0x87, + 0x10, 0x3e, 0x60, 0xf5, 0x9f, 0xdf, 0xa0, 0x30, 0x09, 0x06, 0xf8, 0x1e, 0x74, 0xf8, 0x4c, 0x9b, + 0xf2, 0x1f, 0x2d, 0xfc, 0x02, 0x01, 0x40, 0xa0, 0xff, 0xce, 0x25, 0x4f, 0xe4, 0xc9, 0xc1, 0xed, + 0x80, 0xc0, 0x7f, 0xb5, 0x64, 0xf2, 0xe4, 0x41, 0xff, 0x04, 0x62, 0xaf, 0xc0, 0x27, 0xf4, 0x0c, + 0x85, 0xfa, 0x22, 0x62, 0x10, 0x78, 0xf3, 0xff, 0x80, 0xf3, 0xe0, 0x70, 0x24, 0x12, 0x0f, 0xe8, + 0x7c, 0xe1, 0x3c, 0xe0, 0x79, 0xcb, 0xe4, 0x11, 0xe8, 0x88, 0x3c, 0x56, 0x33, 0x2f, 0xc8, 0x58, + 0x61, 0x52, 0x1e, 0x12, 0xc6, 0x3f, 0xd2, 0xfa, 0x86, 0x7e, 0x0d, 0x02, 0x9d, 0x2b, 0xa6, 0x2f, + 0xc8, 0xd9, 0xc1, 0x20, 0x5c, 0x66, 0x08, 0xf3, 0xf0, 0x79, 0xc9, 0x05, 0x3f, 0xe4, 0x1f, 0x18, + 0xbf, 0xa2, 0xc7, 0x05, 0xc1, 0x40, 0x4b, 0xc5, 0xf3, 0xce, 0x4f, 0x01, 0xeb, 0x73, 0x82, 0xd0, + 0x20, 0x0c, 0x11, 0x07, 0x8f, 0x24, 0x1e, 0xd7, 0xf8, 0x2c, 0xc0, 0x1e, 0x78, 0x3e, 0x48, 0xbc, + 0xab, 0xff, 0x40, 0x79, 0xc3, 0x38, 0x1f, 0xb0, 0xfa, 0x5b, 0xc0, 0xc3, 0xfe, 0x85, 0xf2, 0x3f, + 0xf0, 0x48, 0x1f, 0xc0, 0x1e, 0x7e, 0x30, 0x7a, 0xf4, 0x3f, 0xfc, 0x28, 0x07, 0xe0, 0x9e, 0x60, + 0xf1, 0x37, 0x9c, 0x2f, 0x21, 0x0f, 0x9f, 0xc0, 0x3f, 0x9f, 0xef, 0xfc, 0x3f, 0x05, 0xf7, 0x90, + 0x52, 0x09, 0xe5, 0x0b, 0x90, 0x40, 0xa0, 0x90, 0x0b, 0xfa, 0x3e, 0x61, 0x7c, 0x08, 0x0b, 0xc0, + 0x77, 0xe0, 0xfa, 0x80, 0x03, 0xc7, 0xc1, 0xdf, 0x07, 0xef, 0xe1, 0x9d, 0x00, 0xf3, 0x4e, 0x8f, + 0x3a, 0x06, 0x4f, 0x10, 0x03, 0x7d, 0xc1, 0xe3, 0x80, 0xe0, 0xc0, 0xff, 0xe6, 0xf1, 0x0f, 0xcc, + 0x03, 0xfd, 0x01, 0x47, 0x91, 0xc0, 0x41, 0x03, 0x80, 0x07, 0xa8, 0x40, 0x61, 0xeb, 0xf1, 0xff, + 0xfc, 0xc0, 0x42, 0x76, 0x30, 0x79, 0x80, 0x04, 0x1e, 0x50, 0xd0, 0xf9, 0x11, 0x88, 0x1e, 0xa2, + 0xf2, 0x83, 0x01, 0x88, 0x8c, 0x40, 0xf4, 0xe0, 0x7f, 0x01, 0xfb, 0x30, 0x1c, 0x94, 0x1b, 0x39, + 0x88, 0x07, 0xc7, 0x38, 0x1e, 0x7a, 0x2c, 0x24, 0x04, 0x1e, 0xdc, 0x0f, 0xdc, 0xfc, 0x92, 0x20, + 0xf1, 0xc1, 0x40, 0xc1, 0xed, 0xfc, 0xfd, 0x87, 0xd3, 0x03, 0x07, 0x07, 0x94, 0x14, 0x1e, 0x5f, + 0x80, 0x7a, 0x87, 0xd0, 0x1e, 0x7e, 0x0f, 0xaa, 0x20, 0x87, 0xec, 0x0f, 0x38, 0x07, 0xd0, 0x1e, + 0x65, 0xf5, 0x81, 0x03, 0xcf, 0xd8, 0x0f, 0x33, 0xfa, 0x83, 0xfa, 0xff, 0x01, 0x07, 0x9f, 0x60, + 0x1e, 0x50, 0xe5, 0xfa, 0x03, 0xe4, 0x56, 0x46, 0x52, 0xdf, 0xb3, 0x93, 0x50, 0x0f, 0x26, 0x91, + 0x6f, 0xc1, 0xe7, 0x40, 0x07, 0xb4, 0x1d, 0x4e, 0x40, 0x51, 0xaf, 0x83, 0xd2, 0x90, 0x0f, 0x20, + 0x02, 0xcb, 0xe0, 0x12, 0x00, 0x79, 0x62, 0x09, 0x02, 0x00, 0x68, 0xb8, 0x3d, 0x21, 0x04, 0xc2, + 0x00, 0x20, +}; +const uint8_t* const _A_Levelup1_128x64[] = { + _A_Levelup1_128x64_0, + _A_Levelup1_128x64_1, + _A_Levelup1_128x64_2, + _A_Levelup1_128x64_3, + _A_Levelup1_128x64_4, + _A_Levelup1_128x64_5, + _A_Levelup1_128x64_6, + _A_Levelup1_128x64_7, + _A_Levelup1_128x64_8, + _A_Levelup1_128x64_9, + _A_Levelup1_128x64_10}; + +const uint8_t _A_Levelup2_128x64_0[] = { + 0x01, 0x00, 0x34, 0x01, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, 0x38, 0x1f, 0xe0, + 0x1d, 0x97, 0xff, 0xe7, 0x1c, 0x1f, 0x99, 0xf8, 0x1c, 0xfc, 0x1f, 0x96, 0x7f, 0x87, 0x03, 0xf8, + 0x0f, 0xb8, 0xdc, 0x22, 0x01, 0xfb, 0x07, 0xdc, 0x16, 0x09, 0x00, 0xfa, 0x03, 0xee, 0x1d, 0x02, + 0x80, 0x7a, 0x0b, 0xd7, 0x31, 0x07, 0x8f, 0x40, 0x1f, 0x5b, 0xfa, 0x04, 0x06, 0x01, 0xa0, 0x07, + 0xd5, 0x7f, 0x00, 0x0f, 0xe8, 0x26, 0x06, 0x7f, 0x40, 0x07, 0xd4, 0xbe, 0x05, 0x42, 0xa0, 0x03, + 0xf2, 0x03, 0x24, 0xcf, 0xe0, 0x3f, 0xc5, 0xe9, 0x88, 0x50, 0x72, 0x92, 0x0c, 0x08, 0x3e, 0x73, + 0xf1, 0xf9, 0x0d, 0x22, 0xf9, 0x82, 0x07, 0xcd, 0xbe, 0x61, 0x10, 0x94, 0x79, 0xa0, 0x5f, 0xd0, + 0x7c, 0x45, 0xe8, 0x11, 0x09, 0x27, 0x8c, 0x1f, 0x50, 0xb3, 0xf0, 0xc5, 0x3c, 0xe0, 0xfb, 0x80, + 0x40, 0xb2, 0x9f, 0xf0, 0x7d, 0x7a, 0xe0, 0x10, 0xc0, 0x7e, 0x46, 0xe0, 0x77, 0x00, 0x7e, 0x42, + 0xe1, 0x98, 0x0d, 0x2d, 0xfc, 0x1f, 0x71, 0x08, 0x05, 0x3a, 0x7f, 0x01, 0x37, 0xd4, 0x81, 0xfa, + 0x39, 0xf8, 0x01, 0xfe, 0xe0, 0x12, 0x0a, 0x40, 0x3e, 0xa5, 0xef, 0xe2, 0x98, 0x03, 0xee, 0x01, + 0x06, 0x80, 0x10, 0x84, 0x1f, 0x35, 0xf8, 0x04, 0x62, 0x00, 0x00, 0x10, 0x7c, 0xef, 0xe0, 0x1e, + 0x00, 0x7e, 0x5f, 0xc7, 0xc3, 0x0f, 0x07, 0xda, 0x6c, 0x43, 0xc4, 0x0f, 0xb8, 0x28, 0x3f, 0xa1, + 0xa0, 0xfe, 0x8a, 0x8b, 0xfc, 0x1f, 0xe0, 0xfe, 0x2f, 0xa7, 0xe8, 0xff, 0x30, 0x30, 0x7f, 0x83, + 0xcf, 0xe8, 0xaf, 0x91, 0x7a, 0x03, 0xff, 0xd0, 0xdf, 0x90, 0x7a, 0x74, 0x1f, 0xf2, 0xcf, 0x10, + 0x01, 0x68, 0x01, 0xf1, 0x17, 0x07, 0xa5, 0x00, 0x1f, 0x10, 0x90, 0x7a, 0x60, 0x3f, 0x80, 0xd5, + 0xc7, 0xdf, 0xe0, 0xc0, 0xf4, 0x80, 0x7c, 0xc1, 0xeb, 0x77, 0xc0, 0x40, 0x41, 0xe9, 0x01, 0xe6, + 0x03, 0xd7, 0x17, 0x7f, 0x05, 0x08, 0x3d, 0x26, 0x18, 0x00, 0x7a, 0x42, 0xc1, 0x44, 0x0f, 0x4e, + 0x05, 0xfc, 0x1f, 0xa5, 0x11, 0xff, 0x03, 0xcc, +}; +const uint8_t _A_Levelup2_128x64_1[] = { + 0x01, 0x00, 0x17, 0x02, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x2f, 0xf8, 0x17, 0x59, 0xfe, 0xf8, 0x6b, 0x37, + 0xf9, 0xf0, 0x32, 0x7e, 0xc1, 0xac, 0x02, 0x19, 0xff, 0xfe, 0x03, 0xfc, 0x11, 0x5c, 0xfe, 0x7f, + 0xfe, 0xf1, 0x8c, 0x2e, 0x3d, 0xce, 0x80, 0xe7, 0xbe, 0xff, 0x90, 0x7c, 0xc3, 0xe6, 0x18, 0x0b, + 0x0f, 0xfb, 0xf0, 0x61, 0x8c, 0x7c, 0x3e, 0x19, 0x40, 0xc0, 0x43, 0x6f, 0xc1, 0xe8, 0xbf, 0xe3, + 0xa1, 0x52, 0x08, 0x04, 0x0a, 0xfe, 0x83, 0x7f, 0xdf, 0xf6, 0x09, 0x05, 0x88, 0x40, 0x30, 0x89, + 0x28, 0xfb, 0xfb, 0xf0, 0x10, 0x40, 0x78, 0xca, 0x0f, 0xee, 0x00, 0x18, 0xfd, 0x02, 0x05, 0x08, + 0x80, 0x44, 0x3f, 0xc6, 0x08, 0xbf, 0xd2, 0x3f, 0xc0, 0xf2, 0xfe, 0x7f, 0x41, 0xef, 0xf7, 0x90, + 0x40, 0xe1, 0xf0, 0x09, 0x06, 0xf1, 0x3d, 0x3a, 0x88, 0x04, 0x63, 0xf1, 0xa3, 0xfc, 0x03, 0xe6, + 0xa1, 0x10, 0x90, 0x41, 0x28, 0x80, 0xf1, 0xf3, 0xbe, 0x5a, 0xff, 0xb2, 0x88, 0x50, 0x3f, 0x54, + 0x80, 0x78, 0xb0, 0xda, 0xfb, 0xff, 0xd8, 0x42, 0x30, 0x50, 0x5a, 0x8e, 0xe2, 0x1f, 0xfc, 0xf7, + 0x84, 0x17, 0xf9, 0x68, 0x84, 0x40, 0xbc, 0x79, 0x2f, 0xe7, 0xdc, 0xdf, 0x17, 0x88, 0x46, 0x07, + 0xc5, 0xa3, 0xfe, 0x3c, 0x9e, 0xff, 0x38, 0xfc, 0x41, 0xf0, 0x3e, 0x5d, 0x2f, 0xe0, 0x1e, 0xf2, + 0x78, 0x04, 0xe2, 0x19, 0x80, 0xfe, 0xe9, 0x78, 0x1f, 0x00, 0x7b, 0x61, 0xf4, 0x31, 0xc7, 0xf8, + 0xff, 0x64, 0xb0, 0x9f, 0xe7, 0xff, 0xbe, 0x10, 0x58, 0x1b, 0xf8, 0x81, 0xe3, 0x01, 0xff, 0x6f, + 0xf7, 0xe0, 0xf5, 0xd1, 0xc1, 0x30, 0x18, 0x1f, 0xec, 0x4e, 0x13, 0x8f, 0xfb, 0x83, 0xda, 0x1f, + 0xe4, 0x80, 0x65, 0xbf, 0x71, 0x2c, 0x0f, 0x9f, 0xe7, 0xdf, 0x39, 0x1d, 0xee, 0xfd, 0x9f, 0x8a, + 0x40, 0xfc, 0x17, 0xf8, 0x4f, 0x27, 0xff, 0xfb, 0xf7, 0xb8, 0x8e, 0x7f, 0xfc, 0x9f, 0x43, 0x21, + 0x90, 0x0f, 0x82, 0x08, 0x59, 0xaf, 0xff, 0xcf, 0xc3, 0xa2, 0x10, 0x0c, 0x04, 0x1a, 0x53, 0xe0, + 0x07, 0xa9, 0x10, 0x60, 0x10, 0xa9, 0x04, 0x02, 0x01, 0x01, 0x80, 0x83, 0xda, 0xff, 0x1f, 0xd0, + 0x42, 0xa8, 0x00, 0xf1, 0x80, 0x6a, 0x51, 0xe3, 0xf3, 0x08, 0x26, 0x7a, 0x03, 0x12, 0xc0, 0x40, + 0x44, 0x04, 0x8f, 0x1f, 0x39, 0xfd, 0xa2, 0x50, 0x08, 0x30, 0x7c, 0xbf, 0xdf, 0xfe, 0x79, 0xfd, + 0xa4, 0x50, 0x0b, 0xc0, 0x7c, 0x81, 0xee, 0x96, 0x1a, 0x10, 0xf8, 0x83, 0xe2, 0x2f, 0x1f, 0x7f, + 0xec, 0x1e, 0x7c, 0xf8, 0x5e, 0x08, 0x1e, 0x70, 0x11, 0xf0, 0xa3, 0xd0, 0xfc, 0x3a, 0x38, 0x11, + 0x38, 0x83, 0xe3, 0xf7, 0xf1, 0xab, 0x8d, 0x4a, 0x2d, 0xf1, 0x81, 0x40, 0x81, 0xe5, 0xc1, 0xff, + 0xfc, 0x45, 0xc8, 0x04, 0x1d, 0x38, 0x38, 0xe0, 0x1c, 0x99, 0xff, 0xe0, 0xbc, 0xbf, 0x38, 0x7c, + 0x0c, 0x1e, 0x74, 0x28, 0x79, 0x80, 0x49, 0xc9, 0xc3, 0x97, 0x83, 0xd2, 0x15, 0x06, 0x0f, 0x2c, + 0xde, 0x0f, 0x87, 0x17, 0xa4, 0x12, 0x04, 0x1f, 0x1f, 0x78, 0x3c, 0xa9, 0x70, 0xed, 0x00, 0x3d, + 0x34, 0x92, 0x0c, 0xf6, 0xfc, 0xc0, 0x78, 0xb8, 0xe5, 0x00, 0x1e, 0x90, 0x88, 0x05, 0x9f, 0x3e, + 0x2f, 0x38, 0x5e, 0x0e, 0x44, 0x0f, 0x49, 0x84, 0xab, 0x33, 0xce, 0x07, 0xa4, 0x72, 0x30, 0x0f, + 0x4e, 0x04, 0x03, 0x0c, 0x60, 0x48, 0xbf, 0x82, 0x0e, 0x0f, 0x48, 0x07, 0xff, 0x02, 0x0f, 0x50, + 0x48, 0x83, 0xd6, 0x01, 0x80, 0x07, 0x89, 0x40, 0xa5, 0xe0, 0xf6, 0x61, 0x8d, 0x01, 0x22, 0x80, + 0xfa, 0xb3, 0x03, 0xce, 0x82, 0x0f, 0x89, 0x07, 0x00, 0xfe, 0x78, 0x10, 0x7c, 0xf8, 0x3f, 0x22, + 0x02, 0x81, 0x82, 0x04, 0x1e, 0xd9, 0x0a, 0x17, 0xf0, 0x3e, 0x40, +}; +const uint8_t _A_Levelup2_128x64_2[] = { + 0x01, 0x00, 0xed, 0x02, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x3f, 0xe4, 0x1f, 0xdf, 0x40, + 0x43, 0xf3, 0xc0, 0xa9, 0x7f, 0xfb, 0x03, 0xdf, 0x66, 0x05, 0x4d, 0xff, 0xbc, 0x1e, 0xf3, 0xbf, + 0xf0, 0x14, 0xe3, 0xf7, 0x8f, 0xf7, 0xc4, 0x1e, 0xb1, 0x3f, 0xe0, 0x14, 0xe1, 0xf9, 0x02, 0x0b, + 0xc5, 0xc1, 0xeb, 0xcc, 0xff, 0x03, 0xda, 0x1e, 0x0f, 0x48, 0x3f, 0xbf, 0xfd, 0xf8, 0x07, 0xad, + 0xfc, 0x1e, 0x57, 0xff, 0xf4, 0x0f, 0xe7, 0xff, 0x07, 0xaf, 0x8e, 0xff, 0xf8, 0xed, 0xc7, 0xee, + 0x1f, 0xc8, 0x18, 0x34, 0x41, 0xeb, 0xc3, 0x9f, 0xfc, 0x3f, 0x70, 0xfe, 0x07, 0xfe, 0x01, 0x9c, + 0x0b, 0x88, 0x88, 0x41, 0xe7, 0x1f, 0xc8, 0x5f, 0xe0, 0x7c, 0x08, 0x7c, 0x03, 0xf9, 0x7f, 0x9e, + 0x07, 0xd3, 0x8f, 0x17, 0x80, 0x5e, 0xde, 0xe5, 0x0a, 0xff, 0x4f, 0xe3, 0xc0, 0xf4, 0xc7, 0xcf, + 0xe0, 0x11, 0x32, 0x68, 0x84, 0x07, 0xf7, 0x3f, 0x4b, 0xa7, 0xe2, 0x7f, 0x7f, 0x82, 0x70, 0x20, + 0xb9, 0xdd, 0x54, 0x08, 0x3e, 0x21, 0xe4, 0xfc, 0x4d, 0xff, 0x10, 0xe9, 0x86, 0x22, 0xc0, 0x1f, + 0x1b, 0xf8, 0xb8, 0x58, 0x47, 0xfe, 0x99, 0xda, 0x31, 0xd0, 0xcc, 0x46, 0x03, 0x53, 0x82, 0xbd, + 0xe7, 0x83, 0xe9, 0x3f, 0x99, 0xf0, 0x20, 0x90, 0x28, 0x24, 0x06, 0x05, 0x80, 0xbf, 0xe3, 0x2f, + 0xeb, 0xe5, 0x07, 0x8c, 0x04, 0x02, 0x3a, 0x4e, 0x07, 0xbf, 0x00, 0xa6, 0x25, 0xf1, 0xe0, 0x30, + 0x10, 0x00, 0x78, 0xc2, 0x30, 0x13, 0xc0, 0x79, 0x00, 0x0e, 0xfd, 0x80, 0xa0, 0x03, 0xcb, 0x21, + 0x0a, 0x08, 0x88, 0x39, 0x19, 0x83, 0x00, 0x07, 0x8c, 0x0a, 0x05, 0x10, 0x30, 0x0f, 0x03, 0x7e, + 0x0f, 0x3c, 0xe7, 0xbe, 0x03, 0x20, 0x07, 0x96, 0x82, 0x05, 0x00, 0x90, 0x23, 0xd2, 0x33, 0xff, + 0x07, 0xa0, 0x0c, 0x41, 0xe7, 0xc7, 0x07, 0x8c, 0x6b, 0x9d, 0x01, 0x15, 0x88, 0x3c, 0x68, 0x1a, + 0x88, 0x54, 0x00, 0x18, 0x38, 0xb0, 0x10, 0x9e, 0x04, 0x1e, 0xa1, 0xf1, 0xc7, 0x7f, 0xfc, 0x7f, + 0xd0, 0x78, 0xc1, 0xe0, 0x18, 0x00, 0xc8, 0xa0, 0x07, 0x01, 0xaa, 0x85, 0x36, 0x47, 0xce, 0x3f, + 0x28, 0x69, 0x20, 0x71, 0x10, 0x70, 0x78, 0xe3, 0xc2, 0xc3, 0xff, 0x07, 0x18, 0x00, 0x60, 0x38, + 0x0a, 0x54, 0x23, 0xff, 0x87, 0xc8, 0x68, 0xa1, 0x5f, 0x80, 0x7a, 0x8c, 0x06, 0x49, 0x90, 0x80, + 0xd3, 0x24, 0x01, 0x61, 0xfc, 0xfe, 0xc1, 0xeb, 0x8a, 0x92, 0x54, 0x20, 0x15, 0x09, 0x06, 0xa7, + 0xc0, 0xeb, 0x20, 0x71, 0x5f, 0xe1, 0xf0, 0x0a, 0xa4, 0xc2, 0x41, 0x60, 0xa8, 0x40, 0x7e, 0x1e, + 0xff, 0xe8, 0x3c, 0xa1, 0xff, 0xf8, 0xf4, 0xa3, 0xa9, 0x30, 0x91, 0x58, 0xab, 0x1a, 0x9f, 0x87, + 0x1e, 0xff, 0xde, 0xc7, 0x8b, 0x47, 0xd2, 0x1f, 0x1e, 0xa4, 0x42, 0x45, 0x42, 0xac, 0x07, 0xc7, + 0x8f, 0x3f, 0xff, 0x61, 0xff, 0xff, 0x7e, 0xfc, 0x1f, 0x40, 0x0f, 0x41, 0xf8, 0xf9, 0xdf, 0xa3, + 0xe3, 0x7f, 0x98, 0x7c, 0x62, 0x2e, 0x21, 0xae, 0x40, 0x78, 0x00, 0xc2, 0x4f, 0x91, 0x40, 0x43, + 0xe3, 0x1f, 0x75, 0x8d, 0x7f, 0x53, 0x80, 0xf1, 0xbb, 0xd0, 0x00, 0x30, 0x0d, 0x26, 0x7f, 0xff, + 0xcf, 0x97, 0xc0, 0x60, 0x1f, 0x00, 0x50, 0x82, 0x07, 0xff, 0xf3, 0xfe, 0xe0, 0x64, 0x94, 0xf8, + 0x07, 0x42, 0x4f, 0xa9, 0x81, 0x7f, 0x3e, 0xe0, 0xf2, 0xfe, 0x78, 0x3d, 0xf0, 0x1a, 0x24, 0xc2, + 0x01, 0x60, 0x88, 0x07, 0xc7, 0xfc, 0x0f, 0x10, 0xe8, 0xa4, 0xca, 0x01, 0x84, 0x1f, 0x94, 0x3f, + 0x52, 0xdf, 0x10, 0xf0, 0xf9, 0xfe, 0x3f, 0xfe, 0x3c, 0xf2, 0x01, 0x04, 0x88, 0x17, 0xc5, 0xe4, + 0x30, 0x02, 0x20, 0xf7, 0xfc, 0xff, 0x9f, 0xfc, 0x0a, 0x31, 0xa0, 0x82, 0x48, 0x20, 0x00, 0x11, + 0xd4, 0xe0, 0xb3, 0xfc, 0x7f, 0x3f, 0xcf, 0xf9, 0xff, 0x90, 0xc0, 0x03, 0xe2, 0x0f, 0x36, 0xf8, + 0xc3, 0xf8, 0x3e, 0x7b, 0xc0, 0xf4, 0x6f, 0xf0, 0x00, 0xfa, 0xe0, 0x61, 0x50, 0x7f, 0x05, 0xde, + 0x38, 0x7e, 0x38, 0x04, 0x18, 0x7a, 0x24, 0x11, 0x81, 0x81, 0xc7, 0xc1, 0x13, 0xc0, 0x03, 0x87, + 0x9f, 0x80, 0xd5, 0x03, 0xd3, 0x07, 0x0a, 0x85, 0x7c, 0x20, 0x3e, 0x00, 0x78, 0xed, 0xc2, 0xe3, + 0x05, 0xe0, 0x40, 0x23, 0x00, 0x41, 0x41, 0x8f, 0x81, 0x44, 0x2b, 0x21, 0xa5, 0x6a, 0x31, 0x50, + 0x4c, 0x07, 0xe6, 0x19, 0x00, 0xc4, 0xea, 0xb2, 0x41, 0x91, 0xfc, 0x07, 0xc7, 0x15, 0x01, 0xb0, + 0x03, 0xc6, 0x0e, 0xe4, 0x19, 0x8c, 0x03, 0x18, 0x0f, 0x1d, 0xc7, 0xef, 0xff, 0x08, 0x87, 0x46, + 0x20, 0x86, 0xc1, 0x01, 0x0f, 0x07, 0x01, 0x83, 0x81, 0x4a, 0xb9, 0x06, 0xe1, 0x84, 0x7c, 0x20, + 0xe0, 0xc1, 0x83, 0x17, 0x46, 0x03, 0x7f, 0xc0, 0x18, 0x48, 0x5e, 0x25, 0x91, 0x47, 0x88, 0xdc, + 0x40, 0x82, 0x00, 0x1a, 0x06, 0x88, 0xc2, 0x20, 0xf6, 0x00, 0x21, 0xd4, 0x4b, 0xe2, 0x0f, 0x15, + 0x79, 0x83, 0xd7, 0x41, 0xaa, 0x85, 0x2f, 0x87, 0xfe, 0xa9, 0x10, 0x7b, 0x82, 0x4b, 0xfc, 0xbf, + 0x11, 0x29, 0x00, 0x1b, 0x21, 0xaa, 0x95, 0x03, 0x87, 0xfd, 0x94, 0x07, 0xc4, 0x20, 0x3e, 0x5f, + 0xff, 0xc4, 0x2e, 0x02, 0x0d, 0x50, 0x30, 0xe9, 0x07, 0xe3, 0xf6, 0xff, 0xfe, 0x61, 0x70, 0x61, + 0xfa, 0x83, 0xc5, 0xc2, 0x4a, 0xf2, 0x81, 0x9e, 0xc4, 0x1e, 0xbf, 0x8d, 0x46, 0xab, 0x14, 0x0f, + 0x20, +}; +const uint8_t _A_Levelup2_128x64_3[] = { + 0x01, 0x00, 0x2d, 0x03, 0x8f, 0xc0, 0xb8, 0x1f, 0xfb, 0xfd, 0xe2, 0x7f, 0xf8, 0x02, 0x0f, 0xff, + 0xff, 0x03, 0xff, 0x00, 0xc6, 0xff, 0x36, 0xe0, 0x47, 0xfd, 0xff, 0x08, 0xff, 0xf3, 0xff, 0x3f, + 0x05, 0x8f, 0xf0, 0x1e, 0x5f, 0xf8, 0xfe, 0x02, 0xfb, 0xff, 0xf8, 0x43, 0xff, 0x99, 0xfd, 0xf8, + 0x44, 0x81, 0xe7, 0x17, 0x88, 0x7d, 0x37, 0xe0, 0xf2, 0x87, 0xe7, 0xff, 0xe0, 0x11, 0xfe, 0x83, + 0xca, 0x1f, 0x20, 0xf8, 0x0f, 0x46, 0x0f, 0xfe, 0x83, 0xf6, 0xff, 0xfc, 0xf0, 0xfa, 0x47, 0xec, + 0x1e, 0x08, 0xf8, 0x3c, 0xa0, 0x7c, 0x3f, 0xff, 0x9c, 0x1e, 0x93, 0xf8, 0x06, 0x02, 0x1f, 0xf8, + 0x2c, 0x8c, 0x07, 0xc1, 0xff, 0xfd, 0x83, 0xdb, 0xc1, 0x07, 0xfc, 0x40, 0x6f, 0xda, 0x0d, 0x47, + 0xff, 0xf2, 0x0f, 0x4b, 0xf8, 0x7c, 0x60, 0xda, 0x88, 0x0c, 0x72, 0x01, 0xb0, 0xff, 0xe0, 0x01, + 0xe9, 0xff, 0x80, 0x68, 0x21, 0xff, 0xa9, 0x06, 0x02, 0x01, 0xf2, 0xbf, 0x40, 0x12, 0x44, 0x1f, + 0x2b, 0x82, 0xa1, 0x7d, 0x11, 0xf8, 0x05, 0xf8, 0x1e, 0x74, 0x7f, 0x80, 0xc0, 0x75, 0x50, 0xa8, + 0x04, 0x83, 0xf0, 0x0f, 0x1b, 0xe1, 0x48, 0x6f, 0xfb, 0x78, 0x38, 0x3c, 0x40, 0x09, 0xfc, 0x81, + 0x83, 0xcb, 0xfb, 0xbf, 0xbf, 0xcb, 0xbe, 0x1a, 0x8d, 0x56, 0xaa, 0x55, 0x80, 0x85, 0x18, 0xc4, + 0x5e, 0xb3, 0xce, 0x00, 0x7d, 0x7b, 0x80, 0x21, 0xdf, 0xcf, 0xef, 0xee, 0x72, 0x2f, 0x8e, 0xaa, + 0x01, 0xa8, 0xd1, 0x62, 0xa7, 0xfa, 0x0c, 0x06, 0x7d, 0x82, 0x33, 0x8f, 0x3d, 0xfe, 0x04, 0x18, + 0x15, 0x68, 0xc0, 0x1e, 0xa2, 0x02, 0x1f, 0x0f, 0xff, 0xbf, 0x3f, 0xe3, 0xcf, 0xc7, 0x63, 0xca, + 0xd7, 0xe7, 0xf5, 0x58, 0xa8, 0x3e, 0xa3, 0x80, 0x8e, 0x29, 0xfe, 0x3c, 0x2e, 0x30, 0x0b, 0x87, + 0x76, 0xea, 0x73, 0x11, 0x10, 0xd8, 0x01, 0xe5, 0xcf, 0x9f, 0xcf, 0xf5, 0x50, 0x2d, 0x61, 0x80, + 0x75, 0x38, 0xa2, 0x28, 0xda, 0x09, 0x63, 0x9f, 0x1f, 0x0c, 0xf8, 0x3e, 0x35, 0x6a, 0xad, 0x54, + 0x3a, 0x20, 0x01, 0xba, 0x7f, 0xf7, 0xf9, 0xff, 0xbc, 0xfe, 0x7c, 0x9c, 0x1d, 0x5e, 0xbb, 0x57, + 0xa6, 0x85, 0x68, 0xb5, 0x5b, 0x8d, 0xff, 0x7f, 0xde, 0x0f, 0x18, 0x60, 0xe8, 0x43, 0xc3, 0x55, + 0x86, 0x87, 0xcb, 0x31, 0xfd, 0xff, 0xf9, 0xfc, 0xe4, 0xe1, 0xd4, 0x72, 0xb5, 0x41, 0xf1, 0xcd, + 0x42, 0x88, 0x63, 0xe4, 0xff, 0xfe, 0x7c, 0xff, 0x18, 0x38, 0x5c, 0x68, 0x15, 0x5a, 0xbd, 0x5e, + 0x2a, 0x1f, 0x2f, 0x87, 0xcf, 0xf3, 0x27, 0xfc, 0x41, 0xe5, 0x4b, 0xe2, 0x00, 0x16, 0x29, 0x86, + 0x3f, 0x0f, 0x8e, 0xe2, 0x07, 0xfc, 0x87, 0x85, 0xc6, 0xc0, 0x1f, 0x29, 0xa8, 0x7c, 0xbc, 0x13, + 0x9f, 0x74, 0x0f, 0xd1, 0x0f, 0x0d, 0x0e, 0xc8, 0x3c, 0x56, 0xa2, 0xf0, 0xc0, 0xbf, 0x19, 0x8f, + 0xfc, 0x07, 0xf9, 0x06, 0x0b, 0x8d, 0x40, 0x3e, 0x55, 0x50, 0xf9, 0x7e, 0x38, 0x5f, 0xf8, 0x00, + 0x04, 0x47, 0xc6, 0x80, 0x10, 0x21, 0x42, 0xaf, 0x1d, 0x09, 0xfe, 0x00, 0x1e, 0x20, 0x01, 0xec, + 0x07, 0x46, 0xab, 0xdf, 0x03, 0x94, 0xf3, 0x85, 0x0f, 0x07, 0x90, 0x7c, 0x7d, 0x5a, 0xaf, 0xfe, + 0xbf, 0x74, 0x1f, 0x19, 0x54, 0xf3, 0xd0, 0xb5, 0x1e, 0x1a, 0xdc, 0xfa, 0xb5, 0x5a, 0xed, 0xd6, + 0x02, 0x1f, 0x2f, 0x00, 0x0c, 0x78, 0xf0, 0x1d, 0x03, 0x78, 0x7f, 0x5a, 0xa0, 0x08, 0xea, 0x47, + 0xe3, 0x38, 0xa0, 0x40, 0x25, 0x1e, 0x34, 0xf9, 0x55, 0x2e, 0xa0, 0x01, 0xaa, 0x87, 0xc8, 0x00, + 0x7f, 0xa8, 0x06, 0x8e, 0x15, 0xfc, 0x1e, 0x0f, 0xab, 0xf4, 0x1f, 0x20, 0x71, 0x42, 0xbf, 0x1e, + 0x70, 0xc0, 0xf5, 0x2a, 0xb7, 0x7a, 0xbd, 0x74, 0x3e, 0xdf, 0x8f, 0xfe, 0x0e, 0x54, 0x1f, 0x59, + 0x54, 0xaa, 0x01, 0x08, 0xc0, 0x28, 0xc7, 0xfe, 0x63, 0x10, 0xf8, 0x80, 0x04, 0x3f, 0x08, 0x81, + 0xd5, 0x7f, 0x37, 0xe1, 0xf1, 0xff, 0xea, 0x70, 0x41, 0xf1, 0x90, 0x6a, 0x20, 0x5f, 0xeb, 0xf8, + 0x4c, 0x7e, 0x9d, 0xff, 0xff, 0x7e, 0xfe, 0x1f, 0x6d, 0xf5, 0xff, 0x51, 0x2a, 0xd6, 0x7b, 0xcf, + 0xe3, 0xaf, 0xd4, 0xe0, 0x03, 0xe2, 0xa3, 0x18, 0x17, 0xf2, 0xf6, 0x7f, 0x3a, 0xa9, 0xfe, 0x09, + 0xe3, 0xa8, 0x85, 0x6b, 0x07, 0xe5, 0xaf, 0x80, 0xe0, 0x35, 0x40, 0x11, 0x0f, 0x97, 0xfc, 0x30, + 0x35, 0x42, 0xf9, 0x07, 0xde, 0xfd, 0x0f, 0x0f, 0x93, 0xf0, 0xb5, 0x58, 0x10, 0xe9, 0x07, 0xca, + 0x7d, 0x1f, 0x1f, 0xec, 0xf2, 0x7c, 0x57, 0x10, 0xfa, 0x6a, 0x71, 0x40, 0x53, 0x80, 0x4e, 0x03, + 0xe5, 0x56, 0x1f, 0x9a, 0x98, 0x40, 0x0c, 0xb7, 0x1d, 0x77, 0xab, 0x54, 0xc1, 0x1d, 0x5e, 0x88, + 0x40, 0x48, 0x21, 0xf0, 0x4c, 0x02, 0x23, 0x55, 0xea, 0xaf, 0x86, 0xab, 0x05, 0x80, 0x58, 0x03, + 0xa7, 0xf4, 0xb4, 0x6a, 0xb1, 0x5a, 0xed, 0x57, 0xec, 0x04, 0x38, 0xbd, 0x16, 0xc8, 0x3e, 0x30, + 0xa8, 0x07, 0xf3, 0xb1, 0x01, 0xb0, 0x07, 0xc6, 0xaf, 0x5f, 0xaa, 0xcd, 0x60, 0x1d, 0xc4, 0xfa, + 0x6a, 0x21, 0xdb, 0x80, 0xe0, 0xea, 0xc0, 0x43, 0xeb, 0xc0, 0x6a, 0xc3, 0xe3, 0xa0, 0x85, 0x7f, + 0xab, 0x3f, 0x5a, 0xb9, 0x70, 0x6a, 0xe0, 0x32, 0xbf, 0xf5, 0x43, 0xe7, 0xff, 0xdb, 0x85, 0x44, + 0x38, 0x3f, 0xa3, 0xd9, 0xda, 0xa1, 0x50, 0xa2, 0x00, 0xcf, 0x6a, 0x67, 0xc3, 0x00, 0x28, 0x7c, + 0xf8, 0x1a, 0xf0, 0xf9, 0x80, 0x12, 0x55, 0x28, 0x94, 0x2f, 0xc7, 0xc7, 0x6b, 0x0f, 0x41, 0x84, + 0x40, 0x03, 0x80, 0xf0, 0xf7, 0xc3, 0x8a, 0x00, 0x2c, 0xd0, 0x84, 0x75, 0x5c, 0x2f, 0x8e, 0x7c, + 0x3f, 0x10, 0xd1, 0xf1, 0xfc, 0x20, 0x27, 0x62, 0x00, 0x18, 0x18, 0x74, 0xca, 0x91, 0x1f, 0x8f, + 0xe1, 0xba, 0xae, 0x18, 0xfe, 0x28, 0x44, 0xbe, 0xba, 0xbf, 0xd6, 0xa3, 0x55, 0xa2, 0x0f, 0x8f, + 0xf0, +}; +const uint8_t _A_Levelup2_128x64_4[] = { + 0x01, 0x00, 0x90, 0x02, 0xff, 0x80, 0x3c, 0x01, 0xf1, 0xff, 0xe0, 0x3f, 0x7d, 0xff, 0xd1, 0x0f, + 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x2d, 0xff, 0xee, 0x0f, 0xef, 0x47, 0xff, 0xfc, 0x06, 0x2d, + 0xf8, 0x3d, 0xe0, 0x3e, 0x49, 0x3f, 0xc0, 0xfc, 0x7f, 0x8f, 0xfc, 0xed, 0x02, 0x37, 0xff, 0xc5, + 0xbe, 0x01, 0xf0, 0xaf, 0xe0, 0xc1, 0xe7, 0xe0, 0xff, 0xc0, 0xc2, 0xe3, 0x1f, 0xf8, 0x4f, 0xe0, + 0x17, 0x0b, 0xf5, 0x81, 0x34, 0x40, 0xf1, 0xb8, 0xc1, 0x38, 0x1f, 0xd8, 0x3f, 0xe2, 0x73, 0x00, + 0xf7, 0xc7, 0xe4, 0x02, 0xb1, 0xff, 0xcf, 0xf0, 0x02, 0x81, 0xc0, 0x77, 0xe0, 0x37, 0xf8, 0x47, + 0x82, 0xff, 0x0e, 0x6e, 0x1c, 0x1d, 0xc4, 0x3e, 0x08, 0x0c, 0x0b, 0x80, 0x2e, 0x1f, 0x00, 0x08, + 0x60, 0x06, 0x02, 0xc3, 0xc0, 0x41, 0xe4, 0x09, 0x12, 0x70, 0xd8, 0x23, 0xf8, 0x08, 0x78, 0x3c, + 0x7e, 0xf0, 0x78, 0x1c, 0x03, 0x81, 0x07, 0xff, 0x40, 0xc0, 0xa5, 0x19, 0x10, 0x8a, 0x00, 0xc6, + 0x05, 0xc4, 0xd7, 0xc7, 0xc0, 0x82, 0x00, 0x13, 0xf1, 0xa4, 0xc6, 0x9e, 0x08, 0x3b, 0xf8, 0x3c, + 0xcb, 0xa4, 0x02, 0x13, 0x90, 0xc1, 0xa0, 0x1a, 0xcd, 0x56, 0xa8, 0x84, 0x50, 0x0f, 0x67, 0xff, + 0xe7, 0xf1, 0xf9, 0xfb, 0x3f, 0x30, 0xfa, 0x80, 0xd8, 0xa0, 0x71, 0x40, 0x78, 0xff, 0x39, 0xfe, + 0x7b, 0xff, 0x9f, 0x89, 0xff, 0x85, 0xc0, 0x35, 0x7a, 0xed, 0x58, 0x90, 0x45, 0xe5, 0x87, 0x1f, + 0x94, 0x02, 0x9f, 0x08, 0xca, 0x01, 0x8a, 0x9f, 0x15, 0x07, 0x8d, 0xe3, 0x80, 0x0f, 0x30, 0xc8, + 0xf0, 0x35, 0x41, 0xf1, 0x8d, 0x14, 0x13, 0x9f, 0xfb, 0x07, 0x8c, 0x02, 0x21, 0xd0, 0x0b, 0x15, + 0x7e, 0x2a, 0x51, 0x1b, 0x07, 0xfb, 0xcd, 0xff, 0xe1, 0x9f, 0x8b, 0x40, 0x5e, 0x1d, 0x54, 0xa8, + 0x3c, 0x53, 0xc9, 0xce, 0x43, 0xff, 0x01, 0x44, 0x93, 0xc4, 0x5a, 0xc5, 0x55, 0xa8, 0x40, 0x77, + 0xd1, 0xe8, 0x07, 0xdd, 0xa4, 0x20, 0xf1, 0xd8, 0x07, 0xca, 0xd4, 0x1f, 0x28, 0xf3, 0x80, 0x6f, + 0xf3, 0x05, 0x78, 0xc1, 0x14, 0xe7, 0x7f, 0xaa, 0xd5, 0x60, 0x30, 0x48, 0x06, 0x02, 0x3f, 0xfe, + 0xfe, 0x90, 0x07, 0x51, 0xaa, 0x40, 0x0e, 0xa8, 0xbe, 0x66, 0x91, 0xe3, 0x86, 0x45, 0x3c, 0x3f, + 0x96, 0x39, 0xd5, 0x2a, 0xa1, 0x04, 0xf7, 0xfe, 0xc4, 0xa3, 0xe8, 0xd5, 0x7f, 0xf5, 0xfb, 0xa0, + 0xfa, 0x16, 0x87, 0xc7, 0x6f, 0x80, 0x55, 0x3f, 0xa1, 0xf3, 0x1f, 0x8b, 0xc9, 0x38, 0x70, 0x30, + 0x20, 0xeb, 0x3f, 0x5a, 0xa0, 0x08, 0xb8, 0x0c, 0x1e, 0x58, 0x10, 0x18, 0xd0, 0x03, 0xef, 0x54, + 0x09, 0xab, 0x9c, 0x77, 0x5a, 0xaf, 0x57, 0xe8, 0x16, 0xd1, 0xfb, 0x85, 0xc2, 0x00, 0x1f, 0x50, + 0x80, 0xd5, 0x22, 0x20, 0xf1, 0xff, 0x7e, 0x3f, 0x01, 0xfb, 0xec, 0x7f, 0xef, 0x12, 0x00, 0x78, + 0x87, 0xce, 0x2b, 0x04, 0x82, 0xc5, 0x03, 0xc4, 0x08, 0x1f, 0x1c, 0x07, 0xf9, 0xbf, 0x0f, 0x8b, + 0x60, 0x43, 0xe9, 0x5b, 0x4f, 0x90, 0x7d, 0x6a, 0x35, 0x58, 0x28, 0x04, 0x59, 0xe3, 0xbe, 0xbf, + 0x83, 0xc7, 0x59, 0xef, 0x3f, 0x8e, 0xbc, 0x3e, 0xbf, 0xf8, 0xf8, 0x04, 0x43, 0xeb, 0x55, 0xa2, + 0xd0, 0x6a, 0xb1, 0x58, 0x07, 0xee, 0x13, 0x97, 0x07, 0x8e, 0xb0, 0x7e, 0x41, 0xf5, 0xe0, 0x7f, + 0xe1, 0xb1, 0xf8, 0x7a, 0xfd, 0xc3, 0xe3, 0x05, 0xb0, 0x43, 0xf8, 0x17, 0xf6, 0xf8, 0xeb, 0x35, + 0x40, 0x02, 0x0f, 0xa4, 0xc2, 0x09, 0xe0, 0x8c, 0x40, 0x2a, 0xa1, 0xf8, 0x09, 0x95, 0xc8, 0x2a, + 0x41, 0xf9, 0x00, 0x0c, 0x40, 0x04, 0x35, 0x14, 0xa1, 0x00, 0x06, 0xa7, 0x55, 0xaa, 0x17, 0x84, + 0x00, 0x36, 0x00, 0x19, 0x6b, 0xbd, 0x5a, 0xa6, 0x08, 0xc0, 0xc3, 0xeb, 0xf8, 0x98, 0x40, 0x22, + 0xa1, 0xe3, 0xff, 0xd5, 0xea, 0xb6, 0x53, 0xf9, 0x03, 0xc6, 0xd1, 0x00, 0xc0, 0x6b, 0xb5, 0x5f, + 0xb0, 0x10, 0xc2, 0xe0, 0x1b, 0x00, 0xf9, 0xfb, 0xbf, 0x35, 0x1f, 0x28, 0x7d, 0x27, 0xf5, 0x5f, + 0xe1, 0x00, 0x4c, 0x23, 0xfe, 0xd5, 0xc3, 0xb7, 0x01, 0xc1, 0xd5, 0x80, 0x87, 0xd7, 0x80, 0xd5, + 0x87, 0x8b, 0x01, 0xff, 0xfd, 0x87, 0x50, 0x00, 0x95, 0x44, 0x18, 0xf8, 0x29, 0xfb, 0x33, 0xd0, + 0x87, 0x07, 0xf4, 0x81, 0x3b, 0x50, 0xfc, 0xe1, 0x1f, 0x70, 0xf4, 0x5f, 0xea, 0xb5, 0x03, 0x01, + 0x69, 0xc3, 0xe6, 0x00, 0x49, 0x52, 0x01, 0x3d, 0xe1, 0xf8, 0x30, 0x8c, 0x16, 0x82, 0xdc, 0x1e, + 0x68, 0x71, 0x40, 0x05, 0x1a, 0x0f, 0xa1, 0x84, 0x40, 0x0f, 0x4f, 0x2f, 0x0a, 0xac, 0x42, 0x03, + 0xab, 0x0e, 0xa8, 0x18, 0x60, 0xcc, 0x15, 0x6e, 0xa3, 0x85, 0x6a, 0xb4, 0x16, 0x09, 0x97, 0xd7, + 0x57, 0xfa, 0x0f, 0x28, +}; +const uint8_t _A_Levelup2_128x64_5[] = { + 0x01, 0x00, 0x9c, 0x02, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1f, 0xff, 0xfb, 0xcf, 0xff, + 0xf0, 0x3f, 0xc0, 0x18, 0x7e, 0x1e, 0x30, 0x83, 0xdf, 0xef, 0xd0, 0x0e, 0x27, 0xf6, 0xf7, 0xfc, + 0xe7, 0x83, 0x9b, 0x7f, 0xff, 0x8f, 0xff, 0xa4, 0x63, 0x1c, 0xe3, 0xea, 0xf3, 0xd1, 0xc0, 0x20, + 0xe7, 0xc6, 0x84, 0x2f, 0x5b, 0x97, 0xfe, 0xef, 0xca, 0xf8, 0xf1, 0xe0, 0x20, 0xd6, 0xff, 0x4c, + 0x7d, 0x9a, 0xad, 0xd5, 0x87, 0xe1, 0x4f, 0xc0, 0x7f, 0xff, 0xc3, 0xe4, 0xbe, 0x40, 0x2e, 0xd5, + 0x5b, 0xae, 0x0f, 0xe7, 0x0f, 0xf0, 0x7f, 0xfc, 0x22, 0x04, 0x9b, 0xc0, 0x35, 0x7a, 0xad, 0x56, + 0x1b, 0xff, 0x00, 0xf8, 0x7f, 0x78, 0xe0, 0xf4, 0x9f, 0xe0, 0xaa, 0xa0, 0x01, 0xd6, 0xcf, 0xe0, + 0x04, 0xe1, 0x38, 0x98, 0xf8, 0x67, 0xfc, 0x70, 0xf0, 0xea, 0xa3, 0x50, 0xf0, 0x79, 0x13, 0x93, + 0xa1, 0xff, 0xa3, 0xd5, 0xc3, 0xe3, 0x17, 0x80, 0x40, 0x3f, 0x80, 0xf5, 0x0b, 0x0e, 0xbf, 0x56, + 0x02, 0x19, 0x70, 0x3c, 0x67, 0x07, 0xf6, 0xc7, 0xd9, 0xee, 0xa5, 0xf1, 0x9a, 0xc0, 0x20, 0x90, + 0x03, 0x00, 0xef, 0xfd, 0xe0, 0xf2, 0x87, 0xec, 0x37, 0x5a, 0xaf, 0x55, 0xa8, 0x3e, 0x23, 0x12, + 0xfe, 0xf7, 0xdb, 0xa3, 0x9f, 0x86, 0x74, 0x2b, 0xb5, 0x5e, 0xa8, 0x08, 0x60, 0x20, 0xf1, 0xf8, + 0x10, 0x12, 0x11, 0xa8, 0xd7, 0x17, 0xc7, 0x56, 0x0d, 0x18, 0xe0, 0xc0, 0x8f, 0xa5, 0xff, 0x89, + 0xf6, 0x0b, 0xe5, 0xdf, 0xaa, 0x40, 0x6f, 0xe0, 0xf1, 0xfd, 0xb7, 0x0e, 0x07, 0xe8, 0xd5, 0x7f, + 0xb5, 0x7b, 0xa8, 0x06, 0x02, 0x1f, 0x8c, 0xf1, 0xb0, 0x47, 0xc7, 0xef, 0x07, 0x8f, 0xd7, 0xaf, + 0x57, 0xaf, 0xdf, 0xea, 0xb8, 0x58, 0x7e, 0xff, 0xce, 0x1d, 0x12, 0x10, 0xfc, 0xe0, 0x7e, 0xcf, + 0xd6, 0xab, 0xf5, 0xba, 0x27, 0x0c, 0x0d, 0xbc, 0x26, 0xe2, 0xbf, 0xcb, 0xe0, 0x15, 0x5b, 0xed, + 0x5f, 0xef, 0x55, 0xa0, 0x84, 0x04, 0x1e, 0x30, 0xf7, 0x89, 0x41, 0x8e, 0xc2, 0x7f, 0x2f, 0xd1, + 0xb8, 0xaf, 0xf7, 0xfb, 0xf2, 0xcc, 0x7e, 0x70, 0xe8, 0x07, 0x52, 0xab, 0x57, 0xab, 0xd7, 0x68, + 0xb0, 0x58, 0x00, 0x6c, 0x6f, 0x8b, 0x21, 0xb1, 0xa5, 0x88, 0x3e, 0xc6, 0xe4, 0x17, 0x0d, 0x53, + 0xaa, 0x1f, 0x51, 0x00, 0x8f, 0x4b, 0xff, 0x0c, 0x0f, 0x96, 0xfc, 0x3e, 0x3f, 0xf0, 0xf9, 0x40, + 0x3f, 0xa0, 0xf2, 0xc4, 0xc0, 0xe8, 0x1f, 0x5e, 0xef, 0xff, 0xbf, 0x7d, 0xa2, 0xb0, 0x0b, 0xf1, + 0x7f, 0x10, 0x8f, 0x82, 0x0f, 0x00, 0xd7, 0x7f, 0xf5, 0x7f, 0xed, 0x7c, 0x0c, 0x3c, 0x38, 0x0f, + 0xfc, 0xe7, 0xfd, 0x8d, 0xff, 0xc7, 0xcf, 0xe2, 0xff, 0x2a, 0xa8, 0x7c, 0x68, 0x38, 0x0e, 0xf9, + 0x88, 0x70, 0xe3, 0x11, 0x80, 0xeb, 0xf7, 0xfa, 0xbf, 0x76, 0xbc, 0x4c, 0x20, 0x01, 0x83, 0x08, + 0x45, 0xc0, 0x50, 0x76, 0xe8, 0x80, 0x11, 0x42, 0x58, 0x06, 0x88, 0xff, 0xe7, 0xd0, 0x5d, 0x5b, + 0xf8, 0x40, 0x02, 0x77, 0x10, 0x01, 0x70, 0x3e, 0x2b, 0xf1, 0xbc, 0x4e, 0x2b, 0xb7, 0x5a, 0xbd, + 0xdd, 0x06, 0x31, 0x5a, 0x00, 0x7c, 0x7c, 0x1f, 0xcf, 0xbf, 0xff, 0xd3, 0x78, 0xdd, 0x87, 0x8b, + 0xd4, 0x1f, 0x48, 0x04, 0xc1, 0xc0, 0x57, 0x81, 0xd0, 0xa7, 0xc6, 0xab, 0x05, 0xa0, 0x18, 0x44, + 0x20, 0x58, 0xcc, 0x1c, 0x7f, 0xe1, 0xab, 0xf5, 0x6b, 0x84, 0x42, 0x1f, 0x51, 0x09, 0x43, 0xa8, + 0x17, 0x51, 0xe0, 0x90, 0x86, 0x0b, 0x50, 0xb2, 0x90, 0x86, 0x01, 0xf0, 0x81, 0xda, 0x0c, 0x82, + 0x5f, 0x1f, 0xfe, 0xbf, 0x54, 0xf6, 0x1d, 0x80, 0x74, 0x96, 0x23, 0xd8, 0x80, 0x85, 0xc7, 0xab, + 0x57, 0xaf, 0xd5, 0x66, 0xb5, 0x4e, 0xe5, 0x51, 0xc2, 0x7a, 0x88, 0x76, 0xe0, 0x78, 0x3a, 0xbd, + 0x77, 0xab, 0xdc, 0x26, 0x16, 0x09, 0xdf, 0xab, 0x7f, 0x52, 0xd8, 0x97, 0xce, 0xab, 0x7f, 0xea, + 0xd1, 0x20, 0x14, 0x0f, 0x84, 0xff, 0x6e, 0x7b, 0x11, 0xf8, 0xfe, 0x80, 0x83, 0xae, 0xd5, 0x5a, + 0x87, 0xe5, 0xe0, 0x9f, 0xd5, 0xdb, 0x43, 0x5d, 0x5e, 0x11, 0x88, 0x40, 0xe2, 0x3e, 0x18, 0x07, + 0x9e, 0xff, 0xad, 0x5e, 0x18, 0x00, 0x83, 0xe3, 0x2a, 0x07, 0x0f, 0xf3, 0x9f, 0xff, 0xb7, 0x85, + 0xc4, 0xb8, 0x21, 0xf2, 0x13, 0x8c, 0xd6, 0xaa, 0x4e, 0x1e, 0x3e, 0x73, 0x9d, 0xba, 0x10, 0x37, + 0xaa, 0x34, 0x5e, 0x1e, 0xff, 0xff, 0xf3, 0xb0, 0x1e, 0x88, 0x7d, 0x7a, 0xb4, 0xda, 0x84, 0x01, + 0x9a, 0x3e, 0x38, 0x02, 0x68, 0x40, 0x0d, 0x07, 0x1f, 0x8d, 0xff, 0xec, 0x0f, 0x1a, 0xb0, 0x7d, + 0xaa, 0xb0, 0x33, 0x00, 0xc7, 0x04, 0x02, 0xb2, 0x10, 0xf3, 0x7e, 0x9b, 0xa7, 0x3b, 0xff, 0x00, +}; +const uint8_t _A_Levelup2_128x64_6[] = { + 0x01, 0x00, 0x54, 0x02, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, 0x5f, 0x78, 0x37, + 0x20, 0x3f, 0xc0, 0x7e, 0x4f, 0xff, 0xde, 0x3e, 0x38, 0xae, 0xf9, 0xf0, 0x1c, 0xe0, 0x7e, 0xf8, + 0xf3, 0x3f, 0xfd, 0x9f, 0xdc, 0x1f, 0xbe, 0x6c, 0x03, 0x31, 0xfd, 0x1f, 0xdc, 0xca, 0x01, 0x60, + 0xfd, 0x03, 0xaa, 0xff, 0x09, 0x80, 0x60, 0x3e, 0x80, 0xfa, 0xdf, 0xc1, 0x20, 0x10, 0x0f, 0x48, + 0x3e, 0xab, 0xf0, 0x20, 0x78, 0xf4, 0x81, 0xd3, 0xf7, 0x07, 0xfc, 0xbf, 0x03, 0xff, 0x87, 0xe9, + 0x36, 0xe0, 0xf8, 0xf1, 0xcb, 0xec, 0x30, 0x09, 0x86, 0x93, 0x7f, 0xf7, 0x0f, 0xc6, 0x5e, 0x21, + 0x00, 0xa0, 0x52, 0x23, 0xe0, 0xe7, 0xfe, 0x83, 0xc6, 0x10, 0x6f, 0x1a, 0x46, 0xfc, 0x5f, 0x1f, + 0xcc, 0x59, 0xbc, 0xb1, 0x3b, 0xe4, 0x6c, 0x03, 0xc6, 0x0e, 0x0f, 0x1c, 0x7e, 0xfb, 0x07, 0xdf, + 0x59, 0xad, 0xfe, 0xf9, 0x4b, 0x84, 0x7f, 0xb0, 0x79, 0x0f, 0x76, 0x1c, 0x7e, 0xff, 0x8d, 0x2e, + 0x5e, 0x07, 0x4e, 0x97, 0xfd, 0x7f, 0xc0, 0xf6, 0xfd, 0xfe, 0xec, 0xb5, 0x88, 0x17, 0x4a, 0x60, + 0x2c, 0x4f, 0x79, 0xbd, 0x2a, 0xb3, 0x88, 0x17, 0x8a, 0xa0, 0x0f, 0x2f, 0x88, 0x85, 0xaa, 0xb3, + 0x08, 0x15, 0x8a, 0xa1, 0xbd, 0x03, 0x8f, 0xc0, 0x7a, 0x65, 0x02, 0xea, 0xc4, 0x20, 0x54, 0x2a, + 0xc0, 0x20, 0x6f, 0x9f, 0x0f, 0x98, 0x30, 0x7f, 0xd8, 0x7f, 0x1f, 0xf8, 0x1e, 0xb7, 0x78, 0xf7, + 0xe3, 0xf6, 0x20, 0x2c, 0x3f, 0x0f, 0xdf, 0xd5, 0x88, 0x83, 0xc6, 0xb8, 0x1f, 0x18, 0x7f, 0xf0, + 0xc4, 0x90, 0x7c, 0x1d, 0x56, 0x3f, 0x02, 0xe1, 0x55, 0xc7, 0xe3, 0x07, 0x07, 0x13, 0x50, 0xa7, + 0xf0, 0x2d, 0x2e, 0x63, 0xff, 0xcf, 0x94, 0x07, 0xc7, 0xc7, 0xff, 0x8f, 0x84, 0x4a, 0xfb, 0x7e, + 0xb1, 0x49, 0xab, 0xf0, 0x1e, 0xa4, 0x9f, 0x97, 0x3d, 0xf0, 0x16, 0x44, 0x04, 0x21, 0xe1, 0xfe, + 0x5d, 0xa8, 0xb3, 0x08, 0x0d, 0x92, 0x21, 0xf5, 0x8f, 0xfe, 0x2f, 0x7f, 0xcf, 0x08, 0x3c, 0xa0, + 0x17, 0x8c, 0x2c, 0x7e, 0x03, 0xc4, 0x87, 0xfd, 0x61, 0xff, 0xeb, 0xef, 0xf0, 0x64, 0x3f, 0xf8, + 0xec, 0x02, 0xe1, 0x05, 0x88, 0x40, 0x68, 0x90, 0x0f, 0xaa, 0xac, 0xbf, 0x9f, 0xfd, 0xff, 0x7e, + 0x30, 0x0f, 0x18, 0x4c, 0x82, 0x05, 0x22, 0xc0, 0x7d, 0x01, 0x83, 0xfd, 0xf6, 0x7f, 0xaf, 0x7f, + 0xe9, 0xc4, 0x1a, 0x84, 0x0f, 0x48, 0x27, 0xe3, 0x85, 0x6f, 0x09, 0x0f, 0xff, 0xf3, 0x10, 0x87, + 0xc1, 0x22, 0x20, 0xf2, 0xa0, 0x03, 0xc6, 0x86, 0x6e, 0x1f, 0xbf, 0xf7, 0xe7, 0x00, 0x83, 0xc6, + 0x22, 0x34, 0x08, 0x14, 0x48, 0x1e, 0x37, 0xef, 0x3c, 0x0f, 0xfc, 0x43, 0x1f, 0x9f, 0x02, 0x07, + 0xf0, 0x90, 0x83, 0x04, 0x44, 0x30, 0x49, 0xe4, 0x06, 0x01, 0x1c, 0xfe, 0x04, 0x07, 0x67, 0x03, + 0xb8, 0x48, 0x78, 0x10, 0x48, 0xc8, 0x3c, 0x60, 0x16, 0x08, 0x00, 0x08, 0x5e, 0x43, 0x4b, 0x87, + 0x64, 0x24, 0x38, 0x0f, 0xec, 0x36, 0x41, 0x0c, 0x80, 0x64, 0x01, 0xe3, 0x00, 0xf0, 0x7b, 0xf1, + 0x70, 0x1b, 0x04, 0x47, 0x01, 0x07, 0x87, 0xd0, 0x26, 0x90, 0x0c, 0x64, 0xc2, 0x01, 0xf0, 0x58, + 0x0c, 0x2e, 0x1c, 0x4f, 0x18, 0x04, 0x06, 0x09, 0x40, 0xe2, 0x60, 0x30, 0x7b, 0x88, 0x07, 0xfb, + 0xe2, 0x0f, 0x1f, 0x84, 0x1c, 0x1e, 0x30, 0x68, 0x19, 0x48, 0x6f, 0xf8, 0x0c, 0x04, 0x3f, 0xdd, + 0xff, 0xf6, 0x45, 0xe4, 0x10, 0x08, 0x4c, 0x03, 0xf1, 0x00, 0x47, 0x8c, 0x82, 0x81, 0xc0, 0x85, + 0xa4, 0x07, 0x82, 0x0f, 0x40, 0x00, 0x83, 0xd2, 0x23, 0xd8, 0xfc, 0x61, 0xe5, 0xe0, 0xf1, 0x85, + 0x03, 0xd4, 0x10, 0x26, 0xe1, 0xbc, 0x47, 0xe4, 0xe0, 0xf6, 0x81, 0x03, 0xcb, 0xe1, 0x71, 0xfc, + 0x4f, 0xe8, 0x71, 0x00, 0x7b, 0x50, 0x01, 0x03, 0xe0, 0x81, 0x83, 0xc7, 0x0f, 0x08, 0x07, 0xb6, + 0x03, 0x90, 0x05, 0x18, 0x77, 0x3e, 0x5f, 0x03, 0x83, 0x83, 0xda, 0x01, 0xf7, 0xe1, 0xc3, 0xff, + 0x11, 0xbb, 0xe4, 0x16, 0x0c, 0x0f, 0x7f, 0xdf, 0x9e, 0x3f, 0x70, 0xbd, 0xff, 0x0f, 0x64, 0x0f, + 0x5f, 0xc5, 0xc2, 0x79, 0x3c, 0x57, 0x0f, 0xfc, +}; +const uint8_t _A_Levelup2_128x64_7[] = { + 0x01, 0x00, 0xf7, 0x01, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x05, 0xfd, 0x83, 0xfb, 0xbe, 0x20, 0xf0, 0xff, 0x83, 0x72, + 0x01, 0xfe, 0x07, 0xdf, 0xf6, 0x3f, 0xff, 0xf8, 0x83, 0xf3, 0xcf, 0xf8, 0xe7, 0xc0, 0xff, 0xc0, + 0xf9, 0xfc, 0x46, 0x60, 0xd3, 0x00, 0xfe, 0x41, 0xa0, 0x3c, 0x0e, 0xf0, 0x3e, 0xaf, 0xb8, 0x18, + 0x06, 0x03, 0xba, 0x0f, 0xad, 0xfd, 0x06, 0x01, 0x00, 0xed, 0x9e, 0xee, 0x80, 0x0f, 0x1f, 0xa8, + 0x3e, 0xb5, 0xf2, 0x00, 0x78, 0xfa, 0x81, 0xfb, 0xf8, 0xf5, 0xb6, 0xab, 0x2e, 0x0f, 0x18, 0x3f, + 0x5b, 0x7e, 0x10, 0x9c, 0xbf, 0xfb, 0x01, 0x00, 0xec, 0x07, 0xeb, 0x17, 0x01, 0xa0, 0xc0, 0x76, + 0x01, 0xf9, 0x07, 0x8d, 0xf2, 0x45, 0x02, 0x07, 0xdd, 0x2f, 0x05, 0x14, 0x82, 0x68, 0x01, 0xf3, + 0xe0, 0xa2, 0xe0, 0xe1, 0xb0, 0x4b, 0x03, 0x8c, 0x43, 0xf1, 0x25, 0xa1, 0xc2, 0x61, 0x16, 0x08, + 0x5f, 0xe8, 0x1f, 0x1a, 0x59, 0xe4, 0x2b, 0x11, 0x6f, 0x0f, 0xcf, 0xff, 0x4b, 0x24, 0x85, 0x5a, + 0x2e, 0x04, 0xfa, 0xd1, 0x03, 0xc6, 0x31, 0xa0, 0xdf, 0x07, 0xe7, 0x44, 0x2f, 0x18, 0x67, 0x03, + 0x7d, 0xfe, 0x07, 0xc5, 0x0a, 0x87, 0x0a, 0x89, 0x60, 0x01, 0xc3, 0xfd, 0x06, 0xac, 0xff, 0xff, + 0x46, 0xc1, 0xf3, 0x64, 0xf9, 0xc1, 0x7c, 0x5f, 0xfd, 0xd8, 0x6f, 0x14, 0xfe, 0x51, 0xa3, 0xef, + 0x0f, 0xe7, 0x15, 0x1c, 0x80, 0x7a, 0x38, 0x03, 0xe9, 0xff, 0xc2, 0xa1, 0x70, 0x09, 0x47, 0x03, + 0xfb, 0xf7, 0x07, 0xc4, 0x4a, 0x09, 0x00, 0x8c, 0x50, 0x09, 0xa3, 0xef, 0x1f, 0x28, 0x1c, 0x41, + 0xe5, 0x08, 0x07, 0x97, 0x8c, 0x3f, 0x30, 0x18, 0x34, 0x80, 0x1e, 0x3f, 0xbf, 0x38, 0x3d, 0xe0, + 0x18, 0x68, 0x08, 0x3c, 0x84, 0x0b, 0xf8, 0x18, 0x89, 0x38, 0x6f, 0x10, 0x08, 0x80, 0x3c, 0xbe, + 0x20, 0xf3, 0xf8, 0x4f, 0xf7, 0xf0, 0x68, 0x00, 0x00, 0x61, 0x00, 0xf1, 0xbf, 0xa8, 0x13, 0xc1, + 0x3f, 0xce, 0x40, 0x81, 0xe5, 0x04, 0x06, 0x21, 0x04, 0x81, 0xe3, 0xbc, 0x2f, 0x08, 0x00, 0x60, + 0x5f, 0x99, 0xfa, 0x44, 0xbc, 0x00, 0x21, 0xe0, 0xff, 0x37, 0xf1, 0x80, 0x79, 0x40, 0x76, 0xf1, + 0xf0, 0x78, 0xef, 0xca, 0x25, 0x07, 0xf8, 0x6f, 0xec, 0x00, 0xf2, 0x80, 0x7a, 0x78, 0xff, 0xff, + 0xbc, 0x3c, 0x44, 0x51, 0xfe, 0x46, 0xff, 0x81, 0x01, 0x07, 0x98, 0xf8, 0xae, 0x3c, 0x05, 0x01, + 0xef, 0xe8, 0x05, 0x03, 0x07, 0x8f, 0x3c, 0x16, 0x38, 0x12, 0xa1, 0xef, 0x81, 0xed, 0xb7, 0x1e, + 0x11, 0x88, 0xc3, 0xeb, 0xed, 0xff, 0xd2, 0xc1, 0x83, 0x0f, 0xff, 0xfc, 0xf3, 0x7f, 0xfa, 0x7c, + 0x84, 0x7e, 0x60, 0xf1, 0xa2, 0x03, 0xc7, 0x1e, 0xa3, 0x1b, 0x2f, 0xfa, 0x66, 0x0f, 0x4c, 0xac, + 0x7e, 0x87, 0xd0, 0x1e, 0x9e, 0x16, 0x03, 0x0c, 0xd0, 0x9b, 0xce, 0xc7, 0xe0, 0xa0, 0x78, 0x3c, + 0x3f, 0xb0, 0x78, 0xc1, 0x7f, 0xd0, 0x3c, 0x88, 0x3c, 0xf0, 0x71, 0xf9, 0x04, 0x00, 0xdc, 0x70, + 0x58, 0x44, 0x02, 0xa2, 0x0f, 0x38, 0x1c, 0x22, 0x41, 0xf0, 0x81, 0xff, 0x92, 0x03, 0x0b, 0x07, + 0x8d, 0x28, 0x1e, 0x70, 0x18, 0x44, 0x43, 0xe0, 0xb2, 0x1c, 0xff, 0xfe, 0x10, 0x0f, 0x19, 0x50, + 0x3d, 0x22, 0x11, 0x08, 0xc5, 0xfb, 0x3c, 0x70, 0x18, 0xbb, 0x74, 0x64, 0xc0, 0xf5, 0xb4, 0x4c, + 0x3f, 0x18, 0xf4, 0x80, 0x83, 0xca, 0x2c, 0x0f, 0x49, 0x07, 0xd3, 0xc1, 0x01, 0xc0, 0x13, 0xc6, + 0x5e, 0x9c, 0x10, 0x7a, 0xd0, 0x3f, 0x5e, 0x18, 0x39, 0x80, 0x80, +}; +const uint8_t _A_Levelup2_128x64_8[] = { + 0x01, 0x00, 0x02, 0x02, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x00, 0xbf, 0xb0, 0x7f, 0x88, 0x3c, 0x3f, 0xe1, 0x0f, 0x00, 0x5c, 0x3f, + 0xc0, 0x1f, 0xdf, 0x8c, 0x7c, 0x1f, 0x97, 0xf8, 0x77, 0xf3, 0xf8, 0x21, 0x10, 0x04, 0xe7, 0xe0, + 0x30, 0x2f, 0x98, 0x7e, 0xed, 0xf0, 0x18, 0x0f, 0xb0, 0x1e, 0x7c, 0xf8, 0x4c, 0xcb, 0xe6, 0x0f, + 0x38, 0xb8, 0x3c, 0x7a, 0x00, 0x73, 0xc7, 0xf2, 0x66, 0x60, 0xf4, 0x85, 0xe0, 0x60, 0x1a, 0x00, + 0xf9, 0x83, 0xde, 0x0b, 0x82, 0x80, 0x50, 0x00, 0xe7, 0x4f, 0x84, 0xc9, 0xbf, 0x20, 0x62, 0xff, + 0x40, 0xa8, 0x50, 0x0f, 0xc7, 0xfb, 0xde, 0x0f, 0x2e, 0x4c, 0x9c, 0x1e, 0xf0, 0x6f, 0xe8, 0x3c, + 0xb9, 0xfc, 0x88, 0x3f, 0xe0, 0xfc, 0x70, 0x1f, 0xf4, 0x02, 0x03, 0xfd, 0xe3, 0x83, 0xcf, 0x9f, + 0xfc, 0x07, 0x95, 0xf8, 0xbc, 0x25, 0x01, 0xfd, 0x07, 0xce, 0x13, 0xce, 0x07, 0x95, 0xff, 0xf9, + 0x80, 0xbe, 0x0c, 0x04, 0x1e, 0x60, 0x06, 0xdf, 0x50, 0x30, 0x52, 0x48, 0x04, 0x11, 0x3d, 0x3b, + 0xe9, 0x06, 0x0e, 0x53, 0x00, 0x60, 0x88, 0x3e, 0x2b, 0xfa, 0x0a, 0x14, 0x68, 0xc0, 0x29, 0x01, + 0xe7, 0xe0, 0xf3, 0x92, 0x0a, 0x7f, 0xc8, 0x1e, 0x35, 0xed, 0x04, 0xf0, 0x1e, 0x30, 0x1f, 0xa0, + 0x3c, 0x7e, 0x79, 0xc9, 0xe0, 0x3c, 0xc3, 0xe3, 0x24, 0x85, 0x70, 0x20, 0x1e, 0x80, 0x79, 0x72, + 0x41, 0xf1, 0x30, 0x80, 0x83, 0xcb, 0x07, 0xc9, 0x17, 0xa7, 0x7c, 0x5e, 0x30, 0xaf, 0xc6, 0x0b, + 0xd6, 0x1f, 0x4b, 0x7b, 0x5c, 0x81, 0xe3, 0xc2, 0x85, 0x43, 0xac, 0xbe, 0xc0, 0x79, 0x78, 0xc1, + 0xeb, 0x20, 0x81, 0xe2, 0xe0, 0x31, 0xa1, 0xf9, 0x83, 0xc4, 0xde, 0x94, 0x1f, 0x15, 0xed, 0x1e, + 0x20, 0xf1, 0x87, 0xe0, 0xbe, 0xf2, 0x0a, 0x41, 0x3c, 0xf0, 0x31, 0xc8, 0x64, 0x02, 0x04, 0x6f, + 0x14, 0x7c, 0x40, 0xa0, 0xb0, 0x83, 0xf9, 0x83, 0xe2, 0x09, 0x02, 0x80, 0x1f, 0xc7, 0xf4, 0x0f, + 0x98, 0x40, 0x3c, 0x66, 0x00, 0xfb, 0x88, 0x60, 0x20, 0x04, 0x01, 0xfe, 0x83, 0xe6, 0x41, 0x00, + 0xc1, 0xc0, 0xf0, 0x1f, 0xe0, 0x7d, 0xdf, 0x20, 0x14, 0x00, 0x41, 0x43, 0xe0, 0x10, 0x0c, 0x00, + 0x18, 0xad, 0xe0, 0xf1, 0x00, 0x0e, 0x80, 0x10, 0x5f, 0x30, 0x10, 0xdf, 0xc0, 0xc5, 0x97, 0x8c, + 0x03, 0xcb, 0xa0, 0x08, 0x28, 0x68, 0x7c, 0x86, 0xc5, 0x17, 0x83, 0x83, 0xcb, 0x30, 0x0f, 0x28, + 0x30, 0x1c, 0x8a, 0xc5, 0x17, 0x37, 0x08, 0x00, 0x6e, 0x80, 0xfa, 0x82, 0x01, 0xcb, 0x20, 0x28, + 0x28, 0x36, 0x73, 0x10, 0x0f, 0x45, 0x68, 0x83, 0xdb, 0x45, 0x84, 0x80, 0x83, 0xc7, 0x7f, 0x17, + 0x4e, 0x09, 0x7c, 0x81, 0xe9, 0x82, 0x81, 0x87, 0xcd, 0x78, 0x20, 0xf7, 0xc0, 0xc1, 0xc1, 0xe7, + 0x40, 0x80, 0x83, 0xcb, 0xbd, 0xbf, 0x0f, 0xc5, 0x00, 0xc0, 0x41, 0xf2, 0xff, 0x0f, 0xd7, 0xff, + 0x1b, 0x07, 0xb7, 0xf0, 0xbe, 0xbd, 0xf9, 0x28, 0x3c, 0xfc, 0x01, 0x82, 0x3f, 0xa7, 0xdf, 0xe3, + 0x22, 0x07, 0x9f, 0x03, 0xf4, 0x10, 0x14, 0x7a, 0xde, 0x24, 0x20, 0xf4, 0xfa, 0x07, 0xc6, 0x1c, + 0xbf, 0x4c, 0xfc, 0x82, 0x40, 0x5f, 0x2e, 0x07, 0x20, 0x1e, 0x23, 0x22, 0x6f, 0x95, 0x9c, 0x1e, + 0xb3, 0x0f, 0xff, 0xe3, 0xf6, 0x0c, 0x13, 0xa0, 0xff, 0xe3, 0x20, 0xf5, 0x4a, 0x0f, 0xdd, 0xce, + 0x5c, 0x0f, 0xfc, 0x06, 0x0d, 0x40, 0xc8, 0x11, 0xca, 0x81, 0x00, 0xff, 0xfa, 0x1c, 0x44, 0x00, + 0x1f, 0xf4, 0x24, 0x1e, 0xd0, 0x4f, 0x47, 0xf7, 0xbe, 0x0f, 0x28, 0x0c, 0x22, 0x81, 0x50, 0x07, + 0xa4, 0x0b, 0xd1, 0xe3, 0x0f, 0x98, +}; +const uint8_t _A_Levelup2_128x64_9[] = { + 0x01, 0x00, 0xfb, 0x01, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x01, 0x3d, 0xe0, 0xff, 0x06, 0xe4, 0x07, + 0xe0, 0x0f, 0xab, 0xff, 0xfe, 0x7f, 0xfe, 0xe7, 0xe0, 0xc3, 0xd7, 0xdf, 0x80, 0xce, 0x03, 0xf9, + 0x67, 0x80, 0x71, 0xbd, 0xc0, 0xfb, 0xad, 0xc0, 0x20, 0x3e, 0xd0, 0x49, 0xf3, 0xe1, 0x33, 0x2f, + 0x98, 0x3c, 0xe8, 0xb8, 0x08, 0x07, 0xe8, 0x1e, 0x78, 0xfe, 0x4c, 0xcc, 0x1e, 0x98, 0x2a, 0x04, + 0x03, 0x88, 0x71, 0x30, 0x7a, 0xcb, 0xef, 0x58, 0x10, 0x78, 0xee, 0x01, 0xe7, 0x4f, 0x84, 0xc9, + 0xbf, 0x21, 0xf2, 0xdf, 0xe0, 0x24, 0x10, 0x0a, 0x30, 0x3d, 0x39, 0x32, 0x70, 0x7a, 0x40, 0x24, + 0x30, 0x0c, 0x0f, 0xfb, 0x8e, 0x4f, 0x2e, 0x44, 0x1f, 0xf0, 0x0f, 0x2a, 0xfc, 0x02, 0x7f, 0x80, + 0xc0, 0x7f, 0xa0, 0x20, 0xf3, 0xe7, 0xff, 0x0a, 0xc5, 0x5f, 0xe0, 0x00, 0x06, 0x01, 0xfd, 0x0f, + 0x9c, 0x27, 0x9c, 0x0f, 0x2b, 0xfc, 0xbe, 0x41, 0x1e, 0x90, 0xc0, 0x7f, 0xaa, 0x19, 0x97, 0xe4, + 0x2c, 0x31, 0x28, 0x17, 0x08, 0x1f, 0x5d, 0x0e, 0x04, 0x3a, 0x05, 0x3a, 0x07, 0xcc, 0x5e, 0x91, + 0xa1, 0x86, 0x41, 0x38, 0xdf, 0xe1, 0xf0, 0xe0, 0xf3, 0x92, 0x0a, 0x7f, 0xc8, 0x1e, 0x52, 0x88, + 0xf4, 0x17, 0x08, 0x40, 0x10, 0x78, 0xfc, 0xf3, 0x93, 0xc0, 0x7a, 0xc4, 0xa0, 0xb4, 0x86, 0x09, + 0x03, 0xc7, 0x92, 0x0f, 0x8f, 0xc0, 0x3c, 0xf0, 0x7c, 0x91, 0x7c, 0x43, 0x38, 0x3f, 0xb0, 0xfa, + 0x5b, 0xc0, 0xc3, 0xff, 0x9d, 0x93, 0xc6, 0x09, 0x7f, 0xf9, 0x03, 0xcf, 0xc6, 0x0f, 0x4a, 0x5f, + 0x43, 0xe1, 0xc2, 0xc0, 0x7f, 0x2f, 0xa0, 0xf4, 0x37, 0x9e, 0x2f, 0x21, 0x0d, 0x9f, 0xc0, 0x3f, + 0xf3, 0xef, 0xf4, 0x3f, 0x05, 0xf7, 0x90, 0x52, 0x07, 0xe5, 0x0b, 0x90, 0xc0, 0xa0, 0x90, 0x0b, + 0xfd, 0xfd, 0x1f, 0x10, 0xbe, 0x14, 0x06, 0x01, 0x80, 0xdf, 0x83, 0xa0, 0x1e, 0x30, 0x40, 0x78, + 0xf8, 0x3b, 0xe0, 0xe8, 0xfb, 0xc2, 0xe1, 0x00, 0xf1, 0xfc, 0x2f, 0xa4, 0x1e, 0x54, 0x08, 0x07, + 0x3a, 0x06, 0xbf, 0x10, 0x03, 0x78, 0xc1, 0xe3, 0x80, 0xe0, 0xc0, 0xff, 0xeb, 0xf1, 0x0f, 0xbc, + 0x34, 0xba, 0x3f, 0xe8, 0x0f, 0xc1, 0x10, 0x50, 0xf8, 0x01, 0x5c, 0x41, 0xe5, 0x12, 0x81, 0x01, + 0xc4, 0xf8, 0x2b, 0xf1, 0xff, 0xfc, 0xc0, 0x42, 0x75, 0x39, 0x11, 0x38, 0x40, 0x04, 0x0f, 0x28, + 0x68, 0x7c, 0x88, 0xc5, 0xc2, 0x07, 0x1c, 0x00, 0x5e, 0x50, 0x60, 0x31, 0x11, 0x8a, 0x05, 0x03, + 0x03, 0x97, 0x03, 0xf8, 0x0f, 0x98, 0x60, 0x1c, 0xa6, 0x03, 0x82, 0x83, 0x67, 0x31, 0x00, 0xf4, + 0x98, 0x03, 0xcb, 0x9c, 0x0f, 0x3d, 0x16, 0x12, 0x02, 0x0f, 0x3e, 0x00, 0xb8, 0x87, 0xc4, 0x0f, + 0x2c, 0x14, 0x0c, 0x1e, 0x70, 0x02, 0x00, 0xff, 0x01, 0xeb, 0x81, 0x83, 0x83, 0xd2, 0x1c, 0xbf, + 0x10, 0xfe, 0x44, 0x9b, 0xe3, 0xf5, 0x1f, 0xd0, 0x3c, 0xa0, 0x1f, 0x42, 0xfc, 0xce, 0xa0, 0x10, + 0x90, 0x78, 0xfb, 0x01, 0xe6, 0x7f, 0x5a, 0x30, 0x3f, 0x6f, 0xf8, 0x18, 0x04, 0x14, 0x0e, 0x3d, + 0x80, 0x79, 0x15, 0x85, 0x7e, 0x77, 0xf8, 0x0e, 0x05, 0xe0, 0x20, 0xf4, 0x15, 0x93, 0x7c, 0xb7, + 0xf0, 0x0c, 0x13, 0xc0, 0xb5, 0x00, 0xf2, 0x39, 0x96, 0xfc, 0x1e, 0x74, 0x40, 0x7b, 0x41, 0xd4, + 0xe4, 0x05, 0x1a, 0xf8, 0x3c, 0x60, 0x20, 0x81, 0xa4, 0x03, 0xc8, 0x00, 0xb2, 0xf8, 0x04, 0xa0, + 0x1e, 0x58, 0x82, 0x40, 0x80, 0x1a, 0x2e, 0x2b, 0x13, 0x39, 0x42, 0x09, 0x84, 0x00, 0x40, +}; +const uint8_t _A_Levelup2_128x64_10[] = { + 0x01, 0x00, 0x06, 0x02, 0xff, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x1e, 0x00, + 0xf0, 0x07, 0x80, 0x3c, 0x01, 0xe0, 0x0f, 0x00, 0x78, 0x03, 0xc0, 0x09, 0xef, 0x06, 0xe4, 0x07, + 0xe0, 0x0f, 0xc9, 0xff, 0xfb, 0x9f, 0xc7, 0x07, 0xdf, 0x7e, 0x03, 0x38, 0x0f, 0xe5, 0x9e, 0x01, + 0xc6, 0xf7, 0x03, 0xee, 0xb7, 0x00, 0x80, 0xff, 0x4a, 0x27, 0xcf, 0x84, 0xcc, 0xbe, 0x60, 0x51, + 0xbf, 0xff, 0xe8, 0xb8, 0x08, 0x07, 0xe8, 0x2e, 0x78, 0xfe, 0x4c, 0xcc, 0x1e, 0x98, 0x2a, 0x04, + 0x03, 0x88, 0x1f, 0x30, 0x7a, 0xcf, 0xef, 0xd8, 0x10, 0x78, 0xee, 0x01, 0xe7, 0x4f, 0x84, 0xc9, + 0xbf, 0x21, 0xf2, 0xdf, 0xe0, 0x24, 0x10, 0x0a, 0x37, 0xfc, 0xe2, 0x54, 0xfe, 0x4c, 0x9c, 0x1e, + 0x90, 0x09, 0x0c, 0x03, 0x03, 0xfe, 0xd5, 0x93, 0xcb, 0x91, 0x07, 0xfc, 0x11, 0x8a, 0xbf, 0x00, + 0x9f, 0xe0, 0x30, 0x17, 0xe8, 0x89, 0x88, 0x41, 0xe3, 0xcf, 0xfe, 0x03, 0xcf, 0x80, 0x00, 0x18, + 0x07, 0xf4, 0x3e, 0x70, 0x9e, 0x70, 0x3c, 0xe5, 0xf2, 0x08, 0xf4, 0x85, 0x00, 0x2b, 0x19, 0x97, + 0xe4, 0x2c, 0x31, 0x28, 0x17, 0x09, 0x63, 0x1f, 0xe9, 0x7f, 0x43, 0x81, 0x0e, 0x81, 0x4e, 0x95, + 0xd3, 0x17, 0xa4, 0x68, 0x61, 0x90, 0x46, 0x09, 0x73, 0xf0, 0x79, 0xc9, 0x05, 0x3f, 0xe4, 0x1f, + 0x18, 0xbc, 0xa2, 0x3d, 0x05, 0xc2, 0x30, 0x4b, 0x9f, 0xf3, 0xce, 0x4f, 0x01, 0xeb, 0x12, 0x82, + 0xd2, 0x18, 0x24, 0x0f, 0x1e, 0x48, 0x3e, 0x3f, 0x00, 0xf3, 0xc1, 0xf2, 0x45, 0xe5, 0x5f, 0xfa, + 0x0b, 0xce, 0x19, 0xc1, 0xfd, 0x87, 0xd2, 0xde, 0x06, 0x1f, 0xf4, 0xec, 0x9e, 0x30, 0x4b, 0xff, + 0xc8, 0x1e, 0x7e, 0x30, 0x7a, 0x52, 0xfa, 0x1f, 0x0e, 0x16, 0x03, 0xf8, 0x4f, 0x30, 0x78, 0x9b, + 0xcf, 0x17, 0x90, 0x86, 0xcf, 0xe0, 0x01, 0x61, 0xff, 0xc3, 0xf0, 0x5f, 0x79, 0x05, 0x20, 0x9e, + 0x50, 0xb9, 0x0c, 0x0a, 0x09, 0x00, 0xbf, 0xa3, 0xe6, 0x17, 0xc2, 0x80, 0xc0, 0x30, 0x1b, 0xf0, + 0x7d, 0x41, 0x01, 0xe3, 0xe0, 0xef, 0x83, 0xea, 0x10, 0x0f, 0x1f, 0xc3, 0x3a, 0x01, 0xe7, 0x40, + 0x80, 0x73, 0xa0, 0x64, 0xf1, 0x00, 0x37, 0xdc, 0x1e, 0x38, 0x0e, 0x0c, 0x0f, 0xfe, 0x6f, 0x10, + 0x03, 0xc3, 0x4b, 0xa3, 0xfe, 0x80, 0xfc, 0x11, 0x02, 0x70, 0x18, 0x01, 0x5c, 0x41, 0xe5, 0x12, + 0x81, 0x01, 0xc7, 0xfd, 0x0f, 0x5f, 0x8f, 0xff, 0xe6, 0x02, 0x13, 0xb1, 0xc8, 0x89, 0xc2, 0x00, + 0x20, 0x79, 0x43, 0x43, 0xe4, 0x46, 0x2e, 0x10, 0x38, 0xe0, 0x02, 0xf2, 0x83, 0x01, 0x88, 0x8c, + 0x50, 0x28, 0x18, 0x1c, 0xb8, 0x1f, 0xc0, 0x7c, 0xc3, 0x00, 0xe5, 0x30, 0x1c, 0x94, 0x1b, 0x39, + 0x88, 0x07, 0xa4, 0xc0, 0x1e, 0x5c, 0xe0, 0x79, 0xe8, 0xb0, 0x90, 0x10, 0x79, 0xf0, 0x05, 0xc5, + 0xfb, 0x9f, 0x92, 0x44, 0x1e, 0x38, 0x28, 0x18, 0x3c, 0xe0, 0x04, 0x01, 0xfe, 0x7e, 0xc3, 0xe9, + 0x81, 0x83, 0x83, 0xd2, 0x1c, 0xbf, 0x10, 0x7a, 0x87, 0xda, 0x24, 0xdf, 0x1f, 0xaa, 0x20, 0x87, + 0xee, 0x0f, 0x28, 0x07, 0xd0, 0x1e, 0x65, 0xf5, 0x9d, 0x40, 0x21, 0x20, 0xf1, 0xf6, 0x03, 0xcc, + 0xfe, 0xb4, 0x60, 0x7e, 0xdf, 0xf0, 0x30, 0x08, 0x28, 0x1c, 0x7b, 0x00, 0xf2, 0x2b, 0x0a, 0xfc, + 0xef, 0xf0, 0x1c, 0x0b, 0xc0, 0x41, 0xe8, 0x2b, 0x23, 0x29, 0x6f, 0xe0, 0x18, 0x27, 0x81, 0x6a, + 0x01, 0xe4, 0xd2, 0x2d, 0xf8, 0x3c, 0xe8, 0x80, 0xf6, 0x83, 0xa9, 0xc8, 0x0a, 0x35, 0xf0, 0x78, + 0xc0, 0x41, 0x03, 0x48, 0x07, 0x90, 0x01, 0x65, 0xf0, 0x09, 0x40, 0x3c, 0xb1, 0x04, 0x81, 0x00, + 0x34, 0x5c, 0x56, 0x26, 0x72, 0x84, 0x13, 0x08, 0x00, 0x80, +}; +const uint8_t* const _A_Levelup2_128x64[] = { + _A_Levelup2_128x64_0, + _A_Levelup2_128x64_1, + _A_Levelup2_128x64_2, + _A_Levelup2_128x64_3, + _A_Levelup2_128x64_4, + _A_Levelup2_128x64_5, + _A_Levelup2_128x64_6, + _A_Levelup2_128x64_7, + _A_Levelup2_128x64_8, + _A_Levelup2_128x64_9, + _A_Levelup2_128x64_10}; + +const uint8_t _I_125_10px_0[] = { + 0x00, 0xe0, 0x00, 0x00, 0x01, 0x0e, 0x02, 0x31, 0x02, 0x45, 0x02, + 0x91, 0x00, 0xaa, 0x00, 0x92, 0x00, 0x44, 0x00, 0x38, 0x00, +}; +const uint8_t* const _I_125_10px[] = {_I_125_10px_0}; + +const uint8_t _I_Apps_10px_0[] = { + 0x01, 0x00, 0x12, 0x00, 0x00, 0x0c, 0x82, 0x01, 0x88, 0x80, 0x6a, + 0xa0, 0x5a, 0x80, 0x19, 0x00, 0x46, 0x44, 0x07, 0x20, 0x78, 0x80, +}; +const uint8_t* const _I_Apps_10px[] = {_I_Apps_10px_0}; + +const uint8_t _I_Nfc_10px_0[] = { + 0x00, 0x80, 0x00, 0x00, 0x01, 0x22, 0x02, 0x43, 0x02, 0x45, 0x02, + 0x49, 0x02, 0x31, 0x02, 0x22, 0x02, 0x00, 0x01, 0x80, 0x00, +}; +const uint8_t* const _I_Nfc_10px[] = {_I_Nfc_10px_0}; + +const uint8_t _I_back_10px_0[] = { + 0x00, 0x00, 0x00, 0x10, 0x00, 0x38, 0x00, 0x7c, 0x00, 0xfe, 0x00, + 0x38, 0x00, 0x38, 0x00, 0xf8, 0x01, 0xf8, 0x01, 0x00, 0x00, +}; +const uint8_t* const _I_back_10px[] = {_I_back_10px_0}; + +const uint8_t _I_badusb_10px_0[] = { + 0x01, 0x00, 0x11, 0x00, 0x00, 0x0f, 0xe2, 0x01, 0xfc, 0x80, 0xdd, + 0x20, 0x32, 0x48, 0x08, 0x14, 0x40, 0x23, 0xa8, 0x08, 0xa0, +}; +const uint8_t* const _I_badusb_10px[] = {_I_badusb_10px_0}; + +const uint8_t _I_dir_10px_0[] = { + 0x01, 0x00, 0x11, 0x00, 0x00, 0x0c, 0xfe, 0x01, 0x41, 0x80, 0x7f, + 0xe0, 0x70, 0x18, 0x10, 0x05, 0x7f, 0xd0, 0x10, 0x88, 0x80, +}; +const uint8_t* const _I_dir_10px[] = {_I_dir_10px_0}; + +const uint8_t _I_ibutt_10px_0[] = { + 0x00, 0x80, 0x03, 0x40, 0x02, 0x20, 0x02, 0x10, 0x01, 0x8e, 0x00, + 0x41, 0x00, 0x2d, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x1e, 0x00, +}; +const uint8_t* const _I_ibutt_10px[] = {_I_ibutt_10px_0}; + +const uint8_t _I_ir_10px_0[] = { + 0x00, 0xfc, 0x00, 0x02, 0x01, 0x79, 0x02, 0x84, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x58, 0x00, 0x78, 0x00, 0xff, 0x03, +}; +const uint8_t* const _I_ir_10px[] = {_I_ir_10px_0}; + +const uint8_t _I_keyboard_10px_0[] = { + 0x00, 0x00, 0x00, 0xff, 0x03, 0x01, 0x02, 0x55, 0x02, 0xa9, 0x02, + 0x55, 0x02, 0x01, 0x02, 0xfd, 0x02, 0xff, 0x03, 0x00, 0x00, +}; +const uint8_t* const _I_keyboard_10px[] = {_I_keyboard_10px_0}; + +const uint8_t _I_loading_10px_0[] = { + 0x00, 0xfe, 0x00, 0x82, 0x00, 0xba, 0x00, 0x54, 0x00, 0x28, 0x00, + 0x28, 0x00, 0x44, 0x00, 0x92, 0x00, 0xba, 0x00, 0xfe, 0x00, +}; +const uint8_t* const _I_loading_10px[] = {_I_loading_10px_0}; + +const uint8_t _I_music_10px_0[] = { + 0x01, 0x00, 0x10, 0x00, 0xf0, 0x00, 0x46, 0x03, 0x20, 0x80, + 0x00, 0x4e, 0x7d, 0x00, 0x9f, 0x80, 0x4a, 0x3c, 0x13, 0x20, +}; +const uint8_t* const _I_music_10px[] = {_I_music_10px_0}; + +const uint8_t _I_sub1_10px_0[] = { + 0x01, 0x00, 0x12, 0x00, 0x81, 0x40, 0x69, 0x30, 0x2c, 0x2c, 0x0b, + 0x6a, 0x01, 0x28, 0x0c, 0x0a, 0x65, 0x01, 0x98, 0x40, 0x00, 0x26, +}; +const uint8_t* const _I_sub1_10px[] = {_I_sub1_10px_0}; + +const uint8_t _I_u2f_10px_0[] = { + 0x00, 0x00, 0x00, 0xfe, 0x01, 0x01, 0x02, 0x0c, 0x00, 0xf2, 0x03, + 0x92, 0x02, 0x0c, 0x00, 0x01, 0x02, 0xfe, 0x01, 0x00, 0x00, +}; +const uint8_t* const _I_u2f_10px[] = {_I_u2f_10px_0}; + +const uint8_t _I_unknown_10px_0[] = { + 0x01, 0x00, 0x12, 0x00, 0xbc, 0x40, 0x39, 0x90, 0x0c, 0x24, 0x03, + 0x81, 0x00, 0xb0, 0x40, 0x26, 0x00, 0x12, 0x00, 0x08, 0x14, 0xc0, +}; +const uint8_t* const _I_unknown_10px[] = {_I_unknown_10px_0}; + +const uint8_t _I_update_10px_0[] = { + 0x00, 0xfe, 0x01, 0x01, 0x02, 0xff, 0x03, 0x01, 0x02, 0x31, 0x02, + 0x79, 0x02, 0xfd, 0x02, 0x31, 0x02, 0x31, 0x02, 0xff, 0x03, +}; +const uint8_t* const _I_update_10px[] = {_I_update_10px_0}; + +const uint8_t _I_BLE_Pairing_128x64_0[] = { + 0x01, 0x00, 0xb7, 0x01, 0x00, 0x6c, 0x38, 0x1f, 0xd0, 0x10, 0x76, 0xe0, 0x03, 0xdd, 0x40, 0x07, + 0xf4, 0x82, 0x01, 0x08, 0x07, 0xf4, 0xc0, 0x1f, 0x91, 0x08, 0x07, 0x00, 0x1f, 0xc0, 0x0d, 0x1e, + 0xe8, 0x3f, 0xc0, 0x03, 0x58, 0x80, 0xcf, 0x11, 0xd9, 0xaf, 0x85, 0x77, 0x01, 0xf7, 0x60, 0xf8, + 0x45, 0xff, 0x05, 0xed, 0x9e, 0x7c, 0x09, 0xdb, 0xe0, 0x2f, 0x78, 0x03, 0x3c, 0x8e, 0xee, 0x8a, + 0x43, 0x81, 0xfb, 0x0c, 0x66, 0xe8, 0xfc, 0x59, 0xba, 0x6f, 0x28, 0x1b, 0xfb, 0xa3, 0x80, 0xfc, + 0xa0, 0x1f, 0xc6, 0x86, 0xbf, 0xc3, 0x78, 0xce, 0x04, 0x19, 0x26, 0x77, 0xfa, 0x43, 0xbe, 0x12, + 0xa0, 0x7e, 0xf8, 0x2a, 0xa2, 0x02, 0xff, 0x89, 0x27, 0x01, 0xbf, 0x99, 0x38, 0x8a, 0xfc, 0x0f, + 0x8e, 0x07, 0xfe, 0x0e, 0x94, 0x2c, 0x07, 0xfc, 0x7f, 0x1f, 0xf5, 0x00, 0xc3, 0x00, 0xe4, 0x31, + 0x13, 0xd1, 0x00, 0x0a, 0xb8, 0x19, 0x25, 0x91, 0xc0, 0x81, 0xe2, 0xb9, 0x4d, 0x5d, 0x78, 0x64, + 0x2e, 0x84, 0x80, 0x61, 0x07, 0x02, 0x3e, 0x2a, 0xa4, 0xa2, 0x00, 0xf2, 0x40, 0x20, 0xe3, 0x21, + 0xa0, 0x62, 0x9f, 0x60, 0x05, 0x02, 0x3e, 0x36, 0x41, 0x66, 0x23, 0x20, 0x51, 0xfc, 0x40, 0x68, + 0x0f, 0x15, 0x90, 0x60, 0x20, 0x1b, 0x09, 0x89, 0x70, 0x46, 0x42, 0x07, 0x14, 0x99, 0x41, 0xe8, + 0x1f, 0x18, 0x0c, 0x07, 0xc1, 0x19, 0xff, 0xc3, 0xce, 0x6b, 0x54, 0x8f, 0xe0, 0x3f, 0x90, 0x78, + 0x17, 0x02, 0x1a, 0x70, 0x39, 0x01, 0xa0, 0xb1, 0x53, 0xb5, 0x88, 0xc7, 0xe0, 0x98, 0x08, 0x3a, + 0xd5, 0xe8, 0x97, 0xd0, 0x78, 0xcf, 0xe1, 0x07, 0xf1, 0x0d, 0x08, 0x00, 0x74, 0x10, 0x80, 0x18, + 0xe8, 0x97, 0xc3, 0xf2, 0xff, 0xc4, 0x03, 0xe3, 0x04, 0x8c, 0x19, 0xcc, 0x00, 0x35, 0x0c, 0x3c, + 0x03, 0xf9, 0x3f, 0xb0, 0x8f, 0xc6, 0x31, 0x0e, 0x0f, 0x90, 0x90, 0xb5, 0x45, 0xc1, 0xf8, 0x4f, + 0xf0, 0xde, 0x18, 0xcc, 0x82, 0x08, 0x1f, 0x22, 0x20, 0xd0, 0x3a, 0xab, 0xd1, 0xe0, 0x5f, 0xa1, + 0x1b, 0x19, 0x8d, 0x02, 0x04, 0x9a, 0x1d, 0x04, 0x28, 0x26, 0x36, 0xa8, 0x05, 0xf0, 0xe0, 0x3f, + 0x04, 0xf8, 0xd0, 0x30, 0x55, 0xfa, 0xad, 0x54, 0x3e, 0x35, 0x09, 0xab, 0xac, 0xbf, 0x2b, 0xf2, + 0x0a, 0x0e, 0xfb, 0x55, 0xaa, 0x0f, 0x94, 0x68, 0x04, 0x30, 0x6f, 0xd3, 0x7c, 0xb0, 0x15, 0x0f, + 0xfd, 0x7f, 0xeb, 0x05, 0x4f, 0x0b, 0x60, 0xa3, 0x1f, 0x28, 0x0b, 0xfc, 0xbc, 0x30, 0x1f, 0xf7, + 0xfe, 0x54, 0x2c, 0x18, 0x30, 0x3c, 0x6f, 0x00, 0xf2, 0x1c, 0x8c, 0xf8, 0x10, 0x3c, 0x00, 0xf8, + 0xd5, 0x5c, 0x05, 0xb8, 0xb0, 0xaa, 0xdb, 0x01, 0x2b, 0x31, 0x0a, 0xdc, 0xa7, 0x00, 0xe6, 0x00, + 0x0c, 0x56, 0x00, 0x7e, 0x10, 0x00, 0xcc, 0x01, 0xf0, 0x1f, 0x1b, 0x40, 0x2e, 0x00, 0x07, 0x16, + 0x10, 0x90, 0x02, 0xe5, 0x90, 0x06, 0x29, 0x00, 0x2a, 0xa9, 0x00, 0x2f, 0x10, 0x02, 0xa5, 0x10, + 0x02, 0xf1, 0x00, 0x2a, 0xa0, 0x0d, 0xc0, 0x00, 0xec, 0x01, 0xfd, 0x60, 0x17, 0x6a, 0xc0, 0x60, + 0x40, 0xfd, 0xc0, 0x30, 0x04, 0x01, 0xb0, 0xb0, 0x7f, 0x45, 0x80, +}; +const uint8_t* const _I_BLE_Pairing_128x64[] = {_I_BLE_Pairing_128x64_0}; + +const uint8_t _I_Ble_connected_15x15_0[] = { + 0x00, 0xe0, 0x03, 0xf8, 0x0f, 0x7c, 0x1f, 0x7e, 0x3e, 0x6e, 0x3d, 0x5f, 0x7b, 0x3f, 0x7d, 0x7f, + 0x7e, 0x3f, 0x7d, 0x5f, 0x7b, 0x6e, 0x3d, 0x7e, 0x3e, 0x7c, 0x1f, 0xf8, 0x0f, 0xe0, 0x03, +}; +const uint8_t* const _I_Ble_connected_15x15[] = {_I_Ble_connected_15x15_0}; + +const uint8_t _I_Ble_disconnected_15x15_0[] = { + 0x00, 0xe0, 0x03, 0x18, 0x0c, 0x84, 0x10, 0x82, 0x21, 0x92, 0x22, 0xa1, 0x44, 0xc1, 0x42, 0x81, + 0x41, 0xc1, 0x42, 0xa1, 0x44, 0x92, 0x02, 0x82, 0x21, 0x84, 0x10, 0x18, 0x0c, 0xe0, 0x03, +}; +const uint8_t* const _I_Ble_disconnected_15x15[] = {_I_Ble_disconnected_15x15_0}; + +const uint8_t _I_Button_18x18_0[] = { + 0x01, 0x00, 0x19, 0x00, 0xfc, 0x7f, 0xe0, 0x10, 0x68, 0x04, 0x06, 0x07, 0x00, 0x81, 0x00, + 0xbc, 0x05, 0xe0, 0x23, 0x83, 0xc0, 0x20, 0x7f, 0xef, 0xfc, 0x07, 0xf8, 0x32, 0x10, +}; +const uint8_t* const _I_Button_18x18[] = {_I_Button_18x18_0}; + +const uint8_t _I_Circles_47x47_0[] = { + 0x01, 0x00, 0x7e, 0x00, 0x00, 0x0f, 0xe2, 0x3e, 0x04, 0x2c, 0x04, 0x1f, 0xc0, 0x05, 0x2b, + 0x00, 0x08, 0x60, 0x60, 0x21, 0x8c, 0x00, 0x86, 0x18, 0x02, 0x18, 0x20, 0x08, 0x62, 0x00, + 0xe4, 0x0a, 0x0e, 0x00, 0x40, 0x70, 0x0a, 0x00, 0xb0, 0xe0, 0x32, 0x00, 0x29, 0xc0, 0x80, + 0xaa, 0x1f, 0x21, 0x39, 0x42, 0x00, 0xa7, 0x08, 0x02, 0xa8, 0xd0, 0x86, 0xc4, 0x05, 0x1f, + 0x84, 0x1c, 0x0a, 0x30, 0x22, 0x28, 0x92, 0x46, 0x40, 0x05, 0x11, 0x61, 0x01, 0x4a, 0x02, + 0x3e, 0x10, 0x28, 0x91, 0x04, 0x02, 0x32, 0x08, 0x08, 0x14, 0xe8, 0x00, 0xf2, 0x09, 0x90, + 0x17, 0xc0, 0xbe, 0x05, 0x41, 0x7a, 0x0e, 0xd4, 0x8e, 0xc5, 0x36, 0x2f, 0x99, 0xad, 0x4e, + 0xea, 0x89, 0xb4, 0xda, 0xab, 0x6d, 0x7e, 0xac, 0xb5, 0x6b, 0xab, 0x8d, 0x9d, 0xea, 0xfb, + 0x5c, 0x04, 0x1f, 0xe0, 0x26, 0x3f, 0xc4, 0x3c, 0x06, 0x20, +}; +const uint8_t* const _I_Circles_47x47[] = {_I_Circles_47x47_0}; + +const uint8_t _I_Left_mouse_icon_9x9_0[] = { + 0x01, 0x00, 0x0f, 0x00, 0xbe, 0x40, 0x35, 0xd0, 0x09, 0x7c, + 0x04, 0x02, 0x7e, 0xf8, 0x08, 0x00, 0x70, 0x40, 0xf2, +}; +const uint8_t* const _I_Left_mouse_icon_9x9[] = {_I_Left_mouse_icon_9x9_0}; + +const uint8_t _I_Ok_btn_9x9_0[] = { + 0x01, 0x00, 0x0f, 0x00, 0xbe, 0x40, 0x30, 0x50, 0x09, 0xcc, + 0x06, 0xfa, 0x01, 0x40, 0x38, 0x82, 0xc4, 0x1e, 0x20, +}; +const uint8_t* const _I_Ok_btn_9x9[] = {_I_Ok_btn_9x9_0}; + +const uint8_t _I_Ok_btn_pressed_13x13_0[] = { + 0x01, 0x00, 0x14, 0x00, 0xfc, 0x40, 0xff, 0x90, 0x78, 0x74, 0x3b, 0xef, + 0x1d, 0xfd, 0xc6, 0xc0, 0x2e, 0x0b, 0x10, 0x78, 0x84, 0xc4, 0x2e, 0x20, +}; +const uint8_t* const _I_Ok_btn_pressed_13x13[] = {_I_Ok_btn_pressed_13x13_0}; + +const uint8_t _I_Pressed_Button_13x13_0[] = { + 0x01, 0x00, 0x12, 0x00, 0xf8, 0x40, 0x7f, 0x90, 0x7f, 0xf4, 0x3c, + 0x02, 0x3f, 0xf8, 0xf8, 0x05, 0xc1, 0xa6, 0x13, 0x10, 0xb8, 0x80, +}; +const uint8_t* const _I_Pressed_Button_13x13[] = {_I_Pressed_Button_13x13_0}; + +const uint8_t _I_Right_mouse_icon_9x9_0[] = { + 0x01, 0x00, 0x0f, 0x00, 0xbe, 0x40, 0x3d, 0x50, 0x0f, 0x4c, + 0x04, 0x02, 0x7e, 0xf8, 0x08, 0x00, 0x70, 0x40, 0xf2, +}; +const uint8_t* const _I_Right_mouse_icon_9x9[] = {_I_Right_mouse_icon_9x9_0}; + +const uint8_t _I_Space_65x18_0[] = { + 0x01, 0x00, 0x26, 0x00, 0xfc, 0x7f, 0xc0, 0x09, 0x7f, 0x80, 0x41, 0x81, 0xeb, 0x80, + 0x80, 0x40, 0xc3, 0x2d, 0x01, 0x04, 0x78, 0x23, 0xc1, 0x1e, 0x08, 0xf0, 0x47, 0x82, + 0x3c, 0x11, 0x70, 0x73, 0xeb, 0x40, 0x7f, 0xc8, 0xf5, 0xff, 0xc0, 0x3f, 0x89, 0x87, +}; +const uint8_t* const _I_Space_65x18[] = {_I_Space_65x18_0}; + +const uint8_t _I_Voldwn_6x6_0[] = { + 0x00, + 0x08, + 0x0c, + 0x2f, + 0x2f, + 0x0c, + 0x08, +}; +const uint8_t* const _I_Voldwn_6x6[] = {_I_Voldwn_6x6_0}; + +const uint8_t _I_Volup_8x6_0[] = { + 0x00, + 0x48, + 0x8c, + 0xaf, + 0xaf, + 0x8c, + 0x48, +}; +const uint8_t* const _I_Volup_8x6[] = {_I_Volup_8x6_0}; + +const uint8_t _I_Clock_18x18_0[] = { + 0x01, 0x00, 0x31, 0x00, 0xe0, 0x43, 0xe0, 0x1f, 0x09, 0xfc, 0x03, 0xf1, 0x7f, 0x80, + 0x47, 0x3c, 0x10, 0x0d, 0xf7, 0xde, 0x02, 0x02, 0x2d, 0xff, 0xde, 0x07, 0x7f, 0xfd, + 0xc0, 0xff, 0xff, 0xc0, 0x11, 0xdf, 0xff, 0x30, 0x3d, 0xff, 0xca, 0x07, 0x3e, 0xfa, + 0x85, 0xc7, 0xe5, 0x01, 0x10, 0x10, 0x98, 0x85, 0x84, 0x32, 0x20, +}; +const uint8_t* const _I_Clock_18x18[] = {_I_Clock_18x18_0}; + +const uint8_t _I_Error_18x18_0[] = { + 0x01, 0x00, 0x2c, 0x00, 0xe0, 0x43, 0xe0, 0x1f, 0x09, 0xfc, 0x03, 0xf1, 0x7f, 0x80, 0x7f, 0x3f, + 0xf0, 0x0e, 0x77, 0x3e, 0x03, 0x8e, 0xe3, 0xc0, 0x63, 0xfe, 0x38, 0x1c, 0xff, 0xe1, 0x03, 0xbf, + 0xfe, 0x00, 0x46, 0x08, 0x20, 0x71, 0x05, 0x08, 0x34, 0x42, 0x02, 0x13, 0x10, 0xb0, 0x86, 0x44, +}; +const uint8_t* const _I_Error_18x18[] = {_I_Error_18x18_0}; + +const uint8_t _I_EviSmile1_18x21_0[] = { + 0x01, 0x00, 0x39, 0x00, 0x86, 0x70, 0x20, 0x10, 0x6c, 0x04, 0x06, 0x0f, 0x80, 0x81, 0xf3, 0xf9, + 0xf0, 0x3f, 0xff, 0xfc, 0x04, 0x7f, 0xef, 0xfc, 0x04, 0x04, 0xbf, 0x7d, 0xfc, 0x0f, 0xcf, 0x9f, + 0x81, 0xf1, 0xf1, 0xf0, 0x3c, 0x3e, 0x1e, 0x07, 0x8f, 0xe3, 0x86, 0x9b, 0xbd, 0xef, 0x80, 0xef, + 0x3e, 0x90, 0x0b, 0xc5, 0xe2, 0x01, 0xf0, 0x9f, 0xc0, 0x38, 0x10, 0xf8, 0x00, +}; +const uint8_t* const _I_EviSmile1_18x21[] = {_I_EviSmile1_18x21_0}; + +const uint8_t _I_EviSmile2_18x21_0[] = { + 0x01, 0x00, 0x37, 0x00, 0x00, 0x14, 0x3b, 0x81, 0x01, 0x83, 0xe0, 0x20, 0x7c, 0xfe, 0x7c, + 0x0f, 0xff, 0xff, 0x01, 0x1f, 0xfb, 0xff, 0x01, 0x01, 0x2f, 0x9f, 0x3f, 0x03, 0xe3, 0xe3, + 0xe0, 0x78, 0x7c, 0x3c, 0x0f, 0x1f, 0xc7, 0x0d, 0x37, 0x3b, 0x99, 0x01, 0xcf, 0x79, 0x20, + 0x33, 0xcf, 0x84, 0x03, 0xf1, 0x7f, 0x80, 0x7c, 0x27, 0xf0, 0x0e, 0x04, 0x3e, 0x00, +}; +const uint8_t* const _I_EviSmile2_18x21[] = {_I_EviSmile2_18x21_0}; + +const uint8_t _I_EviWaiting1_18x21_0[] = { + 0x01, 0x00, 0x34, 0x00, 0x86, 0x70, 0x20, 0x10, 0x6c, 0x04, 0x06, 0x0f, 0x80, 0x81, + 0xf3, 0xf9, 0xf0, 0x3f, 0xff, 0xfc, 0x04, 0x7f, 0xef, 0xfc, 0x04, 0x04, 0xa0, 0xb2, + 0xdb, 0xeb, 0xe0, 0x7b, 0xfd, 0xfc, 0x0f, 0x3f, 0x9f, 0x81, 0xf1, 0xf8, 0xe1, 0xa9, + 0xfe, 0x7f, 0xe0, 0x1f, 0x8b, 0xfc, 0x03, 0xe1, 0x3f, 0x80, 0x70, 0x21, 0xf0, 0x00, +}; +const uint8_t* const _I_EviWaiting1_18x21[] = {_I_EviWaiting1_18x21_0}; + +const uint8_t _I_EviWaiting2_18x21_0[] = { + 0x01, 0x00, 0x31, 0x00, 0x86, 0x70, 0x20, 0x10, 0x6c, 0x04, 0x06, 0x0f, 0x80, 0x81, + 0xf3, 0xf9, 0xf0, 0x3f, 0xff, 0xfc, 0x04, 0x7f, 0xef, 0xfc, 0x04, 0x04, 0xa0, 0xb2, + 0xeb, 0xed, 0xe0, 0x7f, 0x7f, 0xb8, 0x08, 0xf1, 0xf8, 0xf0, 0xd4, 0xff, 0x3f, 0xf0, + 0x0f, 0xc5, 0xfe, 0x01, 0xf0, 0x9f, 0xc0, 0x38, 0x10, 0xf8, 0x00, +}; +const uint8_t* const _I_EviWaiting2_18x21[] = {_I_EviWaiting2_18x21_0}; + +const uint8_t _I_Percent_10x14_0[] = { + 0x00, 0x0c, 0x03, 0x1e, 0x03, 0x33, 0x03, 0xb3, 0x03, 0xde, 0x01, 0xec, 0x00, 0x70, 0x00, + 0x38, 0x00, 0xdc, 0x00, 0xee, 0x01, 0x37, 0x03, 0x33, 0x03, 0xe3, 0x01, 0xc3, 0x00, +}; +const uint8_t* const _I_Percent_10x14[] = {_I_Percent_10x14_0}; + +const uint8_t _I_Smile_18x18_0[] = { + 0x01, 0x00, 0x2d, 0x00, 0xe0, 0x43, 0xe0, 0x1f, 0x09, 0xfc, 0x03, 0xf1, 0x7f, + 0x80, 0x7f, 0x3f, 0xf0, 0x0f, 0xf7, 0xfe, 0x02, 0x02, 0x2f, 0xff, 0xfe, 0x07, + 0xcf, 0xe7, 0xc0, 0xf0, 0xf8, 0x70, 0x11, 0x82, 0x08, 0x1c, 0x41, 0x42, 0xdf, + 0x7d, 0xe0, 0x37, 0xcf, 0xc0, 0x98, 0xc5, 0x84, 0x32, 0x20, +}; +const uint8_t* const _I_Smile_18x18[] = {_I_Smile_18x18_0}; + +const uint8_t _I_UsbTree_48x22_0[] = { + 0x01, 0x00, 0x3c, 0x00, 0x00, 0x14, 0x3c, 0x08, 0x78, 0x08, 0xf8, 0x10, 0xff, 0xe0, 0x59, 0xb0, + 0x04, 0x52, 0xc0, 0x1d, 0x48, 0xc0, 0x9d, 0x00, 0xa7, 0x02, 0x80, 0x41, 0x80, 0xa5, 0x0e, 0x02, + 0xa4, 0xfb, 0xfe, 0x00, 0xa1, 0x49, 0x04, 0x48, 0x0a, 0x81, 0xd1, 0xc0, 0x40, 0x45, 0x26, 0x05, + 0x30, 0x01, 0x41, 0xbe, 0x10, 0x30, 0x2c, 0x7e, 0x3f, 0xe0, 0x59, 0x80, 0x04, 0x50, 0x0a, 0x60, +}; +const uint8_t* const _I_UsbTree_48x22[] = {_I_UsbTree_48x22_0}; + +const uint8_t _I_ActiveConnection_50x64_0[] = { + 0x01, 0x00, 0xf4, 0x00, 0xff, 0x40, 0xc0, 0x43, 0xff, 0xff, 0xc0, 0x3f, 0x30, 0x48, 0x04, 0x0a, + 0x01, 0x80, 0x80, 0x40, 0x40, 0xc3, 0xfa, 0xff, 0xef, 0xc0, 0xc7, 0xaa, 0x8f, 0xec, 0x01, 0xa9, + 0xcf, 0xd6, 0x81, 0x8e, 0x95, 0xc4, 0x03, 0x0c, 0x84, 0x1a, 0x50, 0xfd, 0x04, 0x02, 0xd2, 0x0d, + 0x2f, 0xe0, 0x88, 0x76, 0x20, 0xd2, 0x11, 0x15, 0x84, 0x0c, 0x36, 0xa8, 0xf0, 0x19, 0x06, 0xe6, + 0x14, 0x22, 0x29, 0x88, 0x88, 0xa6, 0x63, 0x02, 0xd5, 0x00, 0x04, 0x7c, 0x7f, 0xdf, 0xfd, 0xfe, + 0x02, 0x06, 0x2a, 0xac, 0x04, 0x02, 0x20, 0x61, 0xc0, 0x7f, 0x01, 0xa3, 0x02, 0x03, 0x11, 0xf0, + 0x80, 0xc6, 0x28, 0x1f, 0xfc, 0x0d, 0x08, 0x40, 0x62, 0x80, 0xa4, 0x9a, 0x80, 0x03, 0x28, 0x82, + 0x86, 0x42, 0xc4, 0x3a, 0x10, 0x04, 0x02, 0x88, 0x4e, 0xe2, 0x64, 0x16, 0x18, 0x01, 0xe8, 0x10, + 0x78, 0xb8, 0x1d, 0x31, 0xb0, 0x50, 0x32, 0x76, 0xe0, 0xc0, 0x64, 0xbb, 0x17, 0x04, 0x00, 0x7a, + 0xc1, 0xb2, 0xf6, 0x05, 0x4a, 0x0d, 0x60, 0xdb, 0x0a, 0x58, 0x6e, 0xd2, 0x78, 0x39, 0x4e, 0xa0, + 0x12, 0x00, 0x65, 0x83, 0x7c, 0x19, 0x7f, 0x86, 0x12, 0xaa, 0xce, 0x04, 0x02, 0x1e, 0x17, 0xfe, + 0x3e, 0x0a, 0x39, 0x08, 0x8c, 0x67, 0x90, 0x88, 0x06, 0x02, 0x41, 0x07, 0x93, 0xdf, 0x95, 0x65, + 0xa0, 0xc5, 0x4a, 0x3f, 0xe3, 0x31, 0x90, 0x68, 0x31, 0xd2, 0xb0, 0x31, 0x44, 0x34, 0x18, 0x89, + 0x50, 0x18, 0xc3, 0x64, 0x06, 0x4b, 0x84, 0x82, 0xfd, 0x26, 0x03, 0x10, 0xc0, 0x80, 0xc3, 0x20, + 0xe0, 0x7f, 0xf3, 0xe5, 0xd2, 0x6e, 0x20, 0x18, 0x70, 0x06, 0xb6, 0x59, 0x98, 0x18, 0x8b, 0xa4, + 0xf8, 0x28, 0x07, 0xf0, 0xde, 0x50, 0xf8, 0x00, +}; +const uint8_t* const _I_ActiveConnection_50x64[] = {_I_ActiveConnection_50x64_0}; + +const uint8_t _I_ButtonCenter_7x7_0[] = { + 0x00, + 0x1c, + 0x22, + 0x5d, + 0x5d, + 0x5d, + 0x22, + 0x1c, +}; +const uint8_t* const _I_ButtonCenter_7x7[] = {_I_ButtonCenter_7x7_0}; + +const uint8_t _I_ButtonDown_7x4_0[] = { + 0x00, + 0x7f, + 0x3e, + 0x1c, + 0x08, +}; +const uint8_t* const _I_ButtonDown_7x4[] = {_I_ButtonDown_7x4_0}; + +const uint8_t _I_ButtonLeftSmall_3x5_0[] = { + 0x00, + 0x04, + 0x06, + 0x07, + 0x06, + 0x04, +}; +const uint8_t* const _I_ButtonLeftSmall_3x5[] = {_I_ButtonLeftSmall_3x5_0}; + +const uint8_t _I_ButtonLeft_4x7_0[] = { + 0x00, + 0x08, + 0x0c, + 0x0e, + 0x0f, + 0x0e, + 0x0c, + 0x08, +}; +const uint8_t* const _I_ButtonLeft_4x7[] = {_I_ButtonLeft_4x7_0}; + +const uint8_t _I_ButtonRightSmall_3x5_0[] = { + 0x00, + 0x01, + 0x03, + 0x07, + 0x03, + 0x01, +}; +const uint8_t* const _I_ButtonRightSmall_3x5[] = {_I_ButtonRightSmall_3x5_0}; + +const uint8_t _I_ButtonRight_4x7_0[] = { + 0x00, + 0x01, + 0x03, + 0x07, + 0x0f, + 0x07, + 0x03, + 0x01, +}; +const uint8_t* const _I_ButtonRight_4x7[] = {_I_ButtonRight_4x7_0}; + +const uint8_t _I_ButtonUp_7x4_0[] = { + 0x00, + 0x08, + 0x1c, + 0x3e, + 0x7f, +}; +const uint8_t* const _I_ButtonUp_7x4[] = {_I_ButtonUp_7x4_0}; + +const uint8_t _I_DFU_128x50_0[] = { + 0x01, 0x00, 0x2e, 0x02, 0x00, 0x57, 0xfe, 0x0e, 0x0e, 0xcf, 0x84, 0x02, 0x70, 0x0f, 0xc8, 0x74, + 0x03, 0x80, 0x0e, 0xbc, 0x7c, 0x04, 0x06, 0x30, 0x30, 0x74, 0xe0, 0x2f, 0xe0, 0x42, 0x82, 0x03, + 0xe7, 0x81, 0xff, 0x02, 0x14, 0x20, 0x1f, 0x3e, 0x00, 0x79, 0xc4, 0x01, 0xfd, 0x20, 0x07, 0xd5, + 0xd4, 0xe2, 0x53, 0xf2, 0x74, 0xff, 0xe1, 0x40, 0x41, 0x87, 0xd8, 0x01, 0xf1, 0x60, 0xf0, 0x43, + 0xca, 0x43, 0xe0, 0xa7, 0x83, 0xe2, 0x30, 0x01, 0x29, 0x84, 0x7b, 0x20, 0x0f, 0x88, 0x30, 0x3c, + 0xb1, 0x90, 0x1d, 0x00, 0xfa, 0x30, 0x3f, 0xf8, 0xcc, 0x02, 0xc6, 0x31, 0x1f, 0x83, 0x49, 0xa8, + 0x16, 0x0a, 0xf4, 0x7f, 0x00, 0x21, 0x1f, 0x04, 0x38, 0x06, 0x20, 0x04, 0x90, 0x46, 0x35, 0xf0, + 0xfa, 0x00, 0xcc, 0x7f, 0x10, 0x14, 0x0b, 0x46, 0x20, 0xd5, 0x70, 0x50, 0xb4, 0x06, 0xf1, 0x00, + 0x9f, 0x03, 0xd7, 0x09, 0x81, 0xd7, 0xc0, 0x8b, 0x85, 0x38, 0xc0, 0x50, 0x41, 0xeb, 0x63, 0xc0, + 0x07, 0xc6, 0x90, 0xbf, 0x2b, 0x05, 0x01, 0xb8, 0xb1, 0x0c, 0x06, 0xae, 0x01, 0x24, 0x6f, 0x94, + 0x42, 0x80, 0xb2, 0x49, 0xc4, 0x33, 0x80, 0x1f, 0x18, 0x93, 0xfc, 0xa1, 0x14, 0x0e, 0x02, 0x9c, + 0x43, 0xc3, 0x07, 0x81, 0xfc, 0x03, 0xe2, 0xc0, 0x28, 0x14, 0x10, 0x5e, 0x3f, 0x03, 0xc0, 0xcf, + 0xf8, 0x10, 0x0f, 0xe5, 0x56, 0x03, 0x05, 0xf0, 0x40, 0x20, 0x20, 0xf2, 0x42, 0x0d, 0xfd, 0x72, + 0x30, 0x0f, 0xf8, 0x7c, 0x41, 0xe3, 0x80, 0x10, 0x0d, 0x00, 0x5c, 0x4a, 0xd1, 0x87, 0xf8, 0x39, + 0xf5, 0x5c, 0x0c, 0x0b, 0xe0, 0x1c, 0x10, 0x78, 0xfc, 0x02, 0x04, 0x20, 0x1f, 0xf7, 0x0f, 0x57, + 0x80, 0x81, 0x5e, 0x13, 0x83, 0x01, 0x1f, 0x97, 0xff, 0xfe, 0x03, 0x2e, 0x07, 0x57, 0x03, 0x01, + 0xbf, 0x1d, 0x45, 0x70, 0x27, 0xe4, 0xff, 0x8c, 0x07, 0xf5, 0x83, 0xe0, 0xcf, 0xe1, 0x00, 0xf6, + 0x10, 0x8c, 0x07, 0xb1, 0x07, 0xc1, 0xfc, 0x63, 0xe5, 0xd2, 0x07, 0x8f, 0x80, 0x1a, 0x21, 0xe1, + 0xc0, 0x71, 0xe0, 0x20, 0xf1, 0x24, 0x88, 0x34, 0x62, 0x00, 0xe3, 0x3f, 0x8d, 0xfe, 0x81, 0x80, + 0xc1, 0xf8, 0x5b, 0xe2, 0x0f, 0x18, 0xc7, 0xf0, 0x1e, 0x50, 0x35, 0xa0, 0xc8, 0x3f, 0x98, 0x30, + 0x70, 0x87, 0x44, 0x1e, 0x21, 0xe3, 0xf8, 0x02, 0x4b, 0xaf, 0x01, 0x81, 0xb3, 0xca, 0x01, 0x1c, + 0x25, 0x94, 0x01, 0x04, 0x58, 0x8d, 0x5c, 0x0b, 0xc6, 0x08, 0x10, 0x78, 0xc3, 0x3f, 0xf0, 0x72, + 0x88, 0x98, 0x8b, 0x89, 0x55, 0x82, 0xc7, 0x9b, 0xe5, 0x00, 0x87, 0x26, 0xc4, 0x46, 0x20, 0xf2, + 0xd1, 0x87, 0xc6, 0x0c, 0xdf, 0x21, 0x50, 0x8a, 0xc7, 0x00, 0x38, 0x2e, 0x04, 0x42, 0xaf, 0x05, + 0x06, 0x0a, 0xb8, 0x70, 0x0f, 0x91, 0x80, 0x5c, 0x03, 0xc5, 0x30, 0x84, 0x6a, 0xe1, 0x40, 0xf1, + 0x7b, 0x0f, 0x00, 0x7a, 0x24, 0x21, 0x07, 0x94, 0x33, 0x09, 0x57, 0x8a, 0x93, 0x85, 0xec, 0x3e, + 0x00, 0x79, 0x0b, 0x88, 0x06, 0x3c, 0x3f, 0xfc, 0xa8, 0x1e, 0x21, 0x91, 0x76, 0x90, 0x90, 0x40, + 0x03, 0xe0, 0xe0, 0x78, 0x3f, 0xd5, 0x58, 0x0e, 0x08, 0x32, 0x3f, 0x88, 0xa8, 0x90, 0x8c, 0x25, + 0x30, 0xbc, 0x7f, 0xb5, 0x50, 0x1b, 0xe0, 0x20, 0x7f, 0x92, 0x33, 0x88, 0x97, 0x4a, 0x07, 0x0c, + 0x9e, 0x5f, 0xeb, 0xaa, 0xf2, 0x74, 0x8d, 0x17, 0x80, 0x06, 0x29, 0xf1, 0xe0, 0x71, 0xfb, 0xfd, + 0x71, 0xd8, 0xff, 0xf8, 0x21, 0x71, 0x04, 0x87, 0x01, 0xc1, 0xa1, 0xff, 0x83, 0xe7, 0xf0, 0xff, + 0xc1, 0x51, 0xe4, 0xdd, 0x1b, 0x07, 0xc2, 0x63, 0xf6, 0x0f, 0x9f, 0xeb, 0x5f, 0x02, 0x77, 0x8a, + 0xc4, 0xa3, 0x17, 0xc8, 0x44, 0x8c, 0x34, 0x20, 0x71, 0xfe, 0x99, 0x04, 0x88, 0x40, 0x01, 0xc3, + 0x47, 0xf0, 0x93, 0x0f, 0xf4, 0x28, 0x0e, 0x3a, 0xad, 0x50, 0x39, 0x30, 0x1f, 0x18, 0x3d, 0x0e, + 0x31, 0xff, 0x3d, 0x0c, 0x02, 0xa8, 0x03, 0x20, 0x01, 0x7e, 0x3f, 0xf8, 0x09, 0x06, 0x33, 0xfe, + 0x1b, 0x50, +}; +const uint8_t* const _I_DFU_128x50[] = {_I_DFU_128x50_0}; + +const uint8_t _I_Warning_30x23_0[] = { + 0x01, 0x00, 0x47, 0x00, 0x80, 0x70, 0x00, 0x65, 0xe0, 0x80, 0x80, 0xc7, 0xe1, 0x03, 0x01, + 0xaf, 0xe2, 0x0e, 0x03, 0x19, 0xe4, 0x3c, 0x06, 0xb3, 0xe8, 0xf8, 0x0c, 0x67, 0xf3, 0xf0, + 0x1a, 0x60, 0x27, 0xf7, 0xf1, 0x50, 0xcf, 0xff, 0xe0, 0x34, 0xf0, 0x00, 0xc6, 0x03, 0xf0, + 0x01, 0x8c, 0x0c, 0x06, 0x7f, 0x80, 0x18, 0xc1, 0xff, 0x9f, 0xff, 0xfc, 0x3c, 0x06, 0x7f, + 0xe0, 0x58, 0xc7, 0xff, 0xe0, 0x31, 0x00, 0x88, 0x00, 0x67, 0xff, 0xe0, 0x18, 0xc7, 0xc0, +}; +const uint8_t* const _I_Warning_30x23[] = {_I_Warning_30x23_0}; + +const uint8_t _A_Loading_24_0[] = { + 0x01, 0x00, 0x37, 0x00, 0x00, 0x17, 0x83, 0xff, 0x0f, 0x90, 0x40, 0x21, 0x1c, 0x0f, 0xfc, + 0x1f, 0x01, 0x00, 0x81, 0x60, 0x35, 0x40, 0x21, 0xaa, 0x00, 0x86, 0x51, 0x02, 0x80, 0x44, + 0x60, 0x30, 0x0c, 0x10, 0x6c, 0x6a, 0x80, 0x21, 0x94, 0x00, 0x92, 0x88, 0x02, 0x1c, 0x90, + 0x08, 0x60, 0x30, 0x11, 0x19, 0x80, 0x9c, 0x64, 0x43, 0x82, 0x1f, 0x11, 0x10, 0x80, +}; +const uint8_t _A_Loading_24_1[] = { + 0x01, 0x00, 0x38, 0x00, 0x00, 0x17, 0x83, 0xff, 0x0f, 0x90, 0x40, 0x21, 0x1c, 0x0f, 0xfc, + 0x1f, 0x01, 0x00, 0x81, 0x00, 0x8e, 0xa8, 0x02, 0x19, 0x44, 0x0a, 0x01, 0x11, 0x80, 0xc0, + 0x30, 0x41, 0xb1, 0xa2, 0x00, 0x86, 0x50, 0x02, 0x40, 0x41, 0x64, 0x80, 0x43, 0x01, 0x80, + 0xe0, 0x22, 0x02, 0x34, 0x01, 0x16, 0xaa, 0x04, 0x32, 0x21, 0xc1, 0x0f, 0x88, 0x88, 0x40, +}; +const uint8_t _A_Loading_24_2[] = { + 0x01, 0x00, 0x36, 0x00, 0x00, 0x17, 0x83, 0xff, 0x0f, 0x90, 0x40, 0x21, 0x1c, 0x0f, 0xfc, + 0x1f, 0x01, 0x00, 0x81, 0x00, 0x9a, 0x51, 0x02, 0x80, 0x44, 0x60, 0x30, 0x0c, 0x10, 0x6c, + 0x68, 0x80, 0x21, 0x94, 0x00, 0x92, 0xa8, 0x02, 0x10, 0x71, 0x05, 0x04, 0x3a, 0x70, 0x80, + 0x10, 0xd5, 0x00, 0x43, 0xaa, 0x81, 0x0c, 0x88, 0x70, 0x43, 0xe2, 0x22, 0x10, +}; +const uint8_t _A_Loading_24_3[] = { + 0x01, 0x00, 0x33, 0x00, 0x00, 0x17, 0x83, 0xff, 0x0f, 0x90, 0x40, 0x21, 0x1c, 0x0f, + 0xfc, 0x1f, 0x01, 0x00, 0x81, 0x00, 0xa2, 0x01, 0x01, 0x80, 0xc0, 0x30, 0x41, 0xb1, + 0xa2, 0x00, 0x86, 0x50, 0x02, 0x40, 0x41, 0x64, 0x80, 0x43, 0x29, 0x80, 0xe0, 0x2a, + 0x81, 0xd1, 0xd5, 0x00, 0x84, 0x0a, 0x83, 0x22, 0x1c, 0x10, 0xf8, 0x88, 0x84, +}; +const uint8_t _A_Loading_24_4[] = { + 0x01, 0x00, 0x45, 0x00, 0x80, 0x50, 0x00, 0x43, 0xe0, 0x02, 0x94, 0x06, 0x01, 0xa0, 0x81, + 0x40, 0x22, 0x10, 0x58, 0x04, 0x22, 0x14, 0x02, 0x18, 0xa0, 0x08, 0x65, 0x00, 0x21, 0xa8, + 0x00, 0x84, 0x30, 0x20, 0x51, 0xf0, 0x41, 0xe1, 0x11, 0x00, 0x18, 0xc2, 0x82, 0x63, 0xa7, + 0x89, 0x40, 0x29, 0x51, 0x58, 0x06, 0x50, 0x0a, 0x32, 0x80, 0x28, 0xd4, 0x00, 0xa3, 0xa0, + 0x84, 0x0d, 0x04, 0x5c, 0x38, 0x01, 0xa0, 0xc0, 0x20, 0xe0, 0x21, 0x02, 0x88, +}; +const uint8_t _A_Loading_24_5[] = { + 0x01, 0x00, 0x2b, 0x00, 0x00, 0x74, 0x1a, 0x01, 0x60, 0x85, 0x40, 0x2a, 0x1f, 0xa8, 0x05, 0x7e, + 0x15, 0x81, 0xa8, 0x42, 0xa8, 0x40, 0x21, 0x92, 0x54, 0x2b, 0x51, 0x8a, 0x86, 0xaa, 0x04, 0x05, + 0x18, 0xc8, 0x14, 0x41, 0x64, 0x11, 0x20, 0xb8, 0x87, 0x4c, 0x46, 0x22, 0x92, 0x1c, 0x58, +}; +const uint8_t _A_Loading_24_6[] = { + 0x01, 0x00, 0x3f, 0x00, 0x80, 0x40, 0x80, 0x43, 0x07, 0x80, 0x60, 0x00, 0xa3, 0x40, + 0x82, 0xc0, 0x34, 0x10, 0x88, 0x05, 0x42, 0x21, 0x00, 0x94, 0x00, 0x87, 0x29, 0x12, + 0x80, 0x52, 0xa2, 0xb0, 0x0d, 0x38, 0x15, 0x22, 0x1e, 0x08, 0x38, 0x04, 0x43, 0x03, + 0x08, 0xa8, 0x00, 0x87, 0xc0, 0x05, 0x49, 0x40, 0x08, 0x62, 0x80, 0x21, 0x85, 0x06, + 0x04, 0x60, 0x43, 0x62, 0x80, 0xc0, 0x02, 0xc2, 0x05, 0x18, 0x00, +}; +const uint8_t* const _A_Loading_24[] = { + _A_Loading_24_0, + _A_Loading_24_1, + _A_Loading_24_2, + _A_Loading_24_3, + _A_Loading_24_4, + _A_Loading_24_5, + _A_Loading_24_6}; + +const uint8_t _A_Round_loader_8x8_0[] = { + 0x00, + 0x3c, + 0x42, + 0x81, + 0x81, + 0x81, + 0x81, + 0x42, + 0x3c, +}; +const uint8_t _A_Round_loader_8x8_1[] = { + 0x00, + 0x3c, + 0x72, + 0xf1, + 0xf1, + 0x81, + 0x81, + 0x42, + 0x3c, +}; +const uint8_t _A_Round_loader_8x8_2[] = { + 0x00, + 0x3c, + 0x72, + 0xf1, + 0xf1, + 0xf1, + 0xf1, + 0x72, + 0x3c, +}; +const uint8_t _A_Round_loader_8x8_3[] = { + 0x00, + 0x3c, + 0x72, + 0xf1, + 0xf1, + 0xff, + 0xff, + 0x7e, + 0x3c, +}; +const uint8_t _A_Round_loader_8x8_4[] = { + 0x00, + 0x3c, + 0x7e, + 0xff, + 0xff, + 0xff, + 0xff, + 0x7e, + 0x3c, +}; +const uint8_t* const _A_Round_loader_8x8[] = { + _A_Round_loader_8x8_0, + _A_Round_loader_8x8_1, + _A_Round_loader_8x8_2, + _A_Round_loader_8x8_3, + _A_Round_loader_8x8_4}; + +const uint8_t _I_DolphinCommon_56x48_0[] = { + 0x01, 0x00, 0xdf, 0x00, 0x00, 0x1f, 0xfe, 0x0e, 0x05, 0x3f, 0x04, 0x06, 0x78, 0x06, 0x30, 0x20, + 0xf8, 0x00, 0xc6, 0x12, 0x1c, 0x04, 0x0c, 0x0a, 0x38, 0x08, 0x08, 0x0c, 0x60, 0xc0, 0x21, 0xe0, + 0x04, 0x0a, 0x18, 0x02, 0x1b, 0x00, 0x18, 0xa3, 0x00, 0x21, 0x90, 0x01, 0x8a, 0x20, 0x02, 0x19, + 0x80, 0x18, 0x80, 0x64, 0x09, 0x20, 0x89, 0x81, 0x8c, 0x3e, 0x41, 0xe2, 0x80, 0x50, 0x00, 0x43, + 0x08, 0x01, 0x0c, 0xfc, 0x68, 0x40, 0x61, 0xc0, 0x50, 0x30, 0x00, 0x63, 0xa0, 0x7f, 0x80, 0xc4, + 0x41, 0x19, 0x07, 0xff, 0x02, 0x06, 0x18, 0x24, 0x03, 0x41, 0xf3, 0x2b, 0x10, 0x19, 0x38, 0x10, + 0x30, 0x31, 0x7f, 0xe0, 0x34, 0x08, 0x30, 0x19, 0x60, 0x80, 0x65, 0x86, 0x0a, 0x4c, 0x0c, 0x30, + 0x81, 0xb9, 0x41, 0xa0, 0x54, 0x08, 0xc7, 0xe2, 0x06, 0x8a, 0x18, 0x25, 0x02, 0x21, 0x0f, 0x19, + 0x88, 0xd8, 0x6e, 0x1b, 0x01, 0xd1, 0x1b, 0x86, 0x39, 0x66, 0x3a, 0xa4, 0x1a, 0x50, 0x06, 0x48, + 0x18, 0x18, 0xd0, 0x03, 0x01, 0x41, 0x98, 0xcc, 0x60, 0x39, 0x01, 0x49, 0x2d, 0x06, 0x03, 0x50, + 0xf8, 0x40, 0x3e, 0x02, 0xc1, 0x82, 0x86, 0xc7, 0xfe, 0x0f, 0x28, 0x2c, 0x91, 0xd2, 0x90, 0x9a, + 0x18, 0x19, 0x3e, 0x6d, 0x73, 0x12, 0x16, 0x00, 0x32, 0x49, 0x72, 0xc0, 0x7e, 0x5d, 0x44, 0xba, + 0x2c, 0x08, 0xa4, 0xc8, 0x82, 0x06, 0x17, 0xe0, 0x81, 0x90, 0x2a, 0x40, 0x61, 0xe1, 0xa2, 0x44, + 0x0c, 0x76, 0x2b, 0xe8, 0x89, 0x26, 0x43, 0x83, 0x31, 0x8c, 0x78, 0x0c, 0xb0, 0x48, 0x10, 0x1a, + 0xe0, 0x00, 0x63, +}; +const uint8_t* const _I_DolphinCommon_56x48[] = {_I_DolphinCommon_56x48_0}; + +const uint8_t _I_ArrowDownEmpty_14x15_0[] = { + 0x01, 0x00, 0x17, 0x00, 0xfc, 0x41, 0xe1, 0x10, 0x40, 0x0c, 0xc3, 0xe7, 0x90, 0x19, + 0x04, 0x0a, 0x20, 0x08, 0x10, 0x48, 0xc4, 0x20, 0x52, 0x08, 0x0f, 0x02, 0x00, +}; +const uint8_t* const _I_ArrowDownEmpty_14x15[] = {_I_ArrowDownEmpty_14x15_0}; + +const uint8_t _I_ArrowDownFilled_14x15_0[] = { + 0x00, 0xf8, 0x07, 0x08, 0x04, 0xe8, 0x05, 0x68, 0x05, 0xa8, 0x05, 0x68, 0x05, 0xa8, 0x05, 0x6f, + 0x3d, 0xa1, 0x21, 0xfa, 0x17, 0xf4, 0x0b, 0xe8, 0x05, 0xd0, 0x02, 0x20, 0x01, 0xc0, 0x00, +}; +const uint8_t* const _I_ArrowDownFilled_14x15[] = {_I_ArrowDownFilled_14x15_0}; + +const uint8_t _I_ArrowUpEmpty_14x15_0[] = { + 0x01, 0x00, 0x18, 0x00, 0xe0, 0x40, 0x24, 0x10, 0x18, 0x84, 0x0a, 0x11, 0x04, 0x82, + 0x42, 0x20, 0x51, 0x08, 0x0c, 0x82, 0x1f, 0x3c, 0x04, 0x88, 0x06, 0x7f, 0x10, 0x70, +}; +const uint8_t* const _I_ArrowUpEmpty_14x15[] = {_I_ArrowUpEmpty_14x15_0}; + +const uint8_t _I_ArrowUpFilled_14x15_0[] = { + 0x00, 0xc0, 0x00, 0x20, 0x01, 0xd0, 0x02, 0xe8, 0x05, 0xf4, 0x0b, 0xfa, 0x17, 0x61, 0x21, 0xaf, + 0x3d, 0x68, 0x05, 0xa8, 0x05, 0x68, 0x05, 0xa8, 0x05, 0xe8, 0x05, 0x08, 0x04, 0xf8, 0x07, +}; +const uint8_t* const _I_ArrowUpFilled_14x15[] = {_I_ArrowUpFilled_14x15_0}; + +const uint8_t _I_DolphinReadingSuccess_59x63_0[] = { + 0x01, 0x00, 0x19, 0x01, 0x00, 0x1d, 0x00, 0x0f, 0xd2, 0x00, 0x21, 0xe0, 0x3f, 0xf0, 0xf9, 0x00, + 0x40, 0xee, 0x00, 0x11, 0x88, 0x04, 0x0e, 0x18, 0x11, 0x18, 0x8c, 0x40, 0x0e, 0x50, 0x30, 0x10, + 0xc0, 0xa1, 0x01, 0xe2, 0x05, 0x14, 0x12, 0x08, 0x33, 0x58, 0x44, 0x08, 0x66, 0xa1, 0xe3, 0x01, + 0x9c, 0x83, 0x00, 0x24, 0x11, 0x11, 0x06, 0xc4, 0x76, 0x20, 0x75, 0x15, 0x99, 0x48, 0xc0, 0xe9, + 0x0f, 0x03, 0x95, 0xfc, 0x86, 0x3c, 0x09, 0x80, 0x1c, 0x7c, 0x00, 0x91, 0x81, 0x48, 0x2f, 0xc1, + 0x41, 0x8c, 0xc0, 0x20, 0x30, 0x1c, 0x87, 0xfc, 0x0e, 0x30, 0x70, 0x70, 0x81, 0xc7, 0xe6, 0x07, + 0x18, 0x08, 0x1c, 0xb9, 0x1e, 0x38, 0x0f, 0x02, 0x01, 0xf0, 0x03, 0xa0, 0xa4, 0x7f, 0x90, 0x30, + 0x38, 0xff, 0xe0, 0x28, 0x21, 0xff, 0x06, 0x44, 0x0e, 0x46, 0xe1, 0x01, 0x8c, 0x03, 0x34, 0x2f, + 0x25, 0x18, 0x80, 0xc7, 0x2a, 0x03, 0x2e, 0x01, 0x3c, 0x70, 0x12, 0xa2, 0x39, 0x78, 0x27, 0xe0, + 0x31, 0xea, 0x82, 0xc4, 0x6c, 0x31, 0xf0, 0x78, 0xea, 0xb0, 0x22, 0x31, 0xfc, 0x1a, 0xc6, 0x01, + 0x55, 0x25, 0x88, 0xf8, 0x4b, 0x02, 0x1f, 0x13, 0xe1, 0x7f, 0x97, 0x85, 0x15, 0x03, 0x90, 0xf8, + 0xa0, 0x10, 0xa1, 0xb1, 0x0e, 0x88, 0x00, 0x7f, 0x0f, 0xc0, 0x7c, 0x57, 0x27, 0x3c, 0xb0, 0x7f, + 0x5f, 0xa9, 0x1f, 0xc0, 0x6a, 0xc5, 0x05, 0xc0, 0xf0, 0x11, 0x46, 0xac, 0x18, 0x3f, 0xf9, 0x54, + 0x75, 0x00, 0x73, 0x1f, 0x0f, 0xfe, 0xfe, 0xc6, 0x30, 0x01, 0xbc, 0x48, 0x00, 0x84, 0x82, 0x00, + 0x1b, 0x64, 0xc0, 0x07, 0x60, 0x03, 0xb4, 0x70, 0x0c, 0xbf, 0x82, 0x31, 0x01, 0x8d, 0x0c, 0x40, + 0x02, 0x37, 0x08, 0x1d, 0x74, 0x00, 0x76, 0xa0, 0x01, 0xdb, 0x01, 0xfe, 0x85, 0x8b, 0x96, 0xaa, + 0x9b, 0x30, 0x01, 0x6a, 0xa3, 0x40, 0x75, 0xaa, 0x03, 0xdb, 0x50, 0xbb, 0x30, 0x01, 0x54, 0x24, + 0x25, 0xe6, 0x51, 0x08, 0x1f, 0x68, 0x00, 0x7f, 0x03, 0xf2, 0x79, 0xc0, 0xf4, +}; +const uint8_t* const _I_DolphinReadingSuccess_59x63[] = {_I_DolphinReadingSuccess_59x63_0}; + +const uint8_t _I_Down_25x27_0[] = { + 0x01, 0x00, 0x46, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x08, 0x24, 0x02, 0x81, 0x00, 0x81, 0x40, + 0x30, 0x10, 0x08, 0x08, 0x38, 0x60, 0x20, 0x3f, 0x01, 0x9f, 0xc7, 0xff, 0x1f, 0x01, 0xa7, + 0x87, 0xff, 0x0f, 0x80, 0xf0, 0x7f, 0xf0, 0x78, 0x0e, 0x07, 0xff, 0x03, 0x0b, 0x8f, 0xfc, + 0x04, 0x30, 0x1f, 0xf0, 0x7c, 0xaf, 0x80, 0x32, 0x9c, 0x00, 0xca, 0x20, 0x37, 0xf0, 0x18, + 0xc0, 0xca, 0x63, 0x01, 0x83, 0x40, 0x38, 0x10, 0x0f, 0xe7, 0xfe, 0xfe, 0x67, 0x40, +}; +const uint8_t* const _I_Down_25x27[] = {_I_Down_25x27_0}; + +const uint8_t _I_Down_hvr_25x27_0[] = { + 0x01, 0x00, 0x3a, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x0f, 0xe7, 0xfe, 0xff, 0x00, 0xff, 0x7f, 0xff, + 0xf0, 0x00, 0x10, 0xff, 0xe0, 0x20, 0x3f, 0x01, 0x9c, 0x3e, 0x01, 0xe0, 0x01, 0xa4, 0x7e, 0x01, + 0xf0, 0x80, 0x8b, 0x47, 0xf1, 0x01, 0x16, 0x8f, 0xf0, 0x2e, 0x23, 0x11, 0x01, 0x88, 0x04, 0xf0, + 0x60, 0x32, 0xe3, 0x80, 0xcb, 0xde, 0x37, 0xf0, 0x1a, 0x95, 0xcc, 0xbe, 0x66, 0x73, +}; +const uint8_t* const _I_Down_hvr_25x27[] = {_I_Down_hvr_25x27_0}; + +const uint8_t _I_InfraredArrowDown_4x8_0[] = { + 0x00, + 0xff, + 0x7e, + 0x3c, + 0x18, +}; +const uint8_t* const _I_InfraredArrowDown_4x8[] = {_I_InfraredArrowDown_4x8_0}; + +const uint8_t _I_InfraredArrowUp_4x8_0[] = { + 0x00, + 0x18, + 0x3c, + 0x7e, + 0xff, +}; +const uint8_t* const _I_InfraredArrowUp_4x8[] = {_I_InfraredArrowUp_4x8_0}; + +const uint8_t _I_InfraredLearnShort_128x31_0[] = { + 0x01, 0x00, 0x10, 0x01, 0x00, 0x47, 0xfb, 0xfe, 0x00, 0x38, 0x38, 0x3e, 0x20, 0x20, 0x54, 0x84, + 0x03, 0x9f, 0xc0, 0x06, 0x58, 0x80, 0x3d, 0xf2, 0x00, 0x65, 0x90, 0x03, 0xde, 0x90, 0x06, 0x5a, + 0x07, 0xc0, 0x8a, 0x70, 0x1a, 0x04, 0x02, 0x51, 0x80, 0x03, 0x94, 0x02, 0x3f, 0x40, 0x20, 0x24, + 0x0b, 0x01, 0x00, 0x92, 0x70, 0x35, 0x40, 0x01, 0xe0, 0xdf, 0xf0, 0x10, 0x40, 0x71, 0x58, 0x20, + 0x90, 0x88, 0x0c, 0x4a, 0x81, 0x55, 0x00, 0x0f, 0x87, 0xf7, 0x00, 0x82, 0x43, 0x36, 0x16, 0xdc, + 0x9c, 0x12, 0x21, 0x01, 0x85, 0x70, 0x3f, 0xc1, 0xf1, 0xf8, 0xfc, 0x60, 0x20, 0xf5, 0x90, 0x40, + 0xa1, 0x34, 0x08, 0x18, 0x7c, 0x7e, 0x24, 0x91, 0x07, 0x8c, 0xc0, 0x5e, 0x52, 0x28, 0x14, 0x17, + 0x81, 0x01, 0x0f, 0x8f, 0xe7, 0xe3, 0x03, 0x1f, 0x8e, 0x02, 0xdb, 0x03, 0x8e, 0x49, 0x20, 0x50, + 0x2e, 0x04, 0x72, 0xbd, 0x55, 0xdc, 0xeb, 0xa0, 0x7c, 0x4f, 0x68, 0xbc, 0x60, 0x72, 0x40, 0x79, + 0x50, 0x23, 0x9a, 0x6d, 0x56, 0x66, 0x5c, 0x0f, 0x21, 0x78, 0x9b, 0x04, 0x1e, 0x28, 0x21, 0x8e, + 0x5c, 0x43, 0xe6, 0x2f, 0x10, 0xf9, 0x0b, 0xc7, 0x04, 0x99, 0x18, 0x06, 0xe0, 0x7e, 0x56, 0x32, + 0x78, 0x8f, 0xc4, 0x08, 0x32, 0x20, 0x79, 0x48, 0x2b, 0x85, 0xf2, 0xf8, 0x83, 0xc4, 0x5c, 0x3f, + 0x03, 0x78, 0xd0, 0x81, 0xe3, 0xc0, 0xdf, 0x9f, 0xcb, 0xf3, 0x04, 0xc6, 0x7d, 0xfb, 0xdf, 0x34, + 0x78, 0xd0, 0x45, 0xe5, 0x7e, 0x4f, 0x97, 0xe2, 0x09, 0x80, 0x07, 0x88, 0xbc, 0x61, 0x00, 0xf3, + 0xd8, 0x2f, 0xcb, 0xe0, 0xcf, 0x60, 0x68, 0xd0, 0x30, 0x15, 0xfa, 0xac, 0x36, 0x3f, 0x60, 0x77, + 0xb3, 0x80, 0x5d, 0xe6, 0x4b, 0x20, 0x03, 0x03, 0xc4, 0x01, 0xd0, 0x10, 0x7f, 0x40, 0x81, 0xfc, + 0xa7, 0x10, 0x06, 0x99, 0xd0, 0x01, 0x51, 0x00, 0x7f, 0x48, 0x01, 0xfd, 0xc0, 0x43, 0x98, 0x00, + 0x8e, 0xfe, 0x00, 0xf0, +}; +const uint8_t* const _I_InfraredLearnShort_128x31[] = {_I_InfraredLearnShort_128x31_0}; + +const uint8_t _I_Mode_25x27_0[] = { + 0x01, 0x00, 0x56, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x08, 0x24, 0x02, 0x81, 0x00, 0x81, 0x40, + 0x30, 0x10, 0x08, 0x08, 0x38, 0x60, 0x20, 0x33, 0xb0, 0xc0, 0x21, 0x90, 0x1f, 0x0c, 0x02, + 0x1c, 0x03, 0x18, 0x0c, 0x3c, 0x06, 0x38, 0x30, 0x19, 0x71, 0xc0, 0x65, 0xef, 0x01, 0x97, + 0xfc, 0x06, 0x5f, 0xe0, 0xb9, 0x5f, 0x00, 0x65, 0x38, 0x01, 0x94, 0x40, 0x06, 0x42, 0xb2, + 0xd0, 0xc0, 0x21, 0x50, 0x1a, 0x09, 0x14, 0x40, 0xe4, 0x0f, 0x10, 0x18, 0xc2, 0x01, 0x68, + 0x47, 0x38, 0x19, 0x4c, 0x60, 0x30, 0x68, 0x07, 0x02, 0x01, 0xfc, 0xff, 0xdf, 0xcc, 0xe8, +}; +const uint8_t* const _I_Mode_25x27[] = {_I_Mode_25x27_0}; + +const uint8_t _I_Mode_hvr_25x27_0[] = { + 0x01, 0x00, 0x51, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x0f, 0xe7, 0xfe, 0xff, 0x00, 0xff, 0x7f, + 0xff, 0xf0, 0x00, 0x10, 0xff, 0xe0, 0x20, 0x33, 0xcf, 0xff, 0xfe, 0x70, 0x18, 0xff, 0xff, + 0xe2, 0x03, 0x1f, 0xf7, 0xc0, 0x06, 0x37, 0xc0, 0x19, 0x4e, 0x00, 0x65, 0x10, 0x01, 0x94, + 0x00, 0x06, 0x50, 0x10, 0xb9, 0x60, 0xc0, 0x65, 0xc7, 0x01, 0x97, 0xbc, 0x06, 0x42, 0xb2, + 0xef, 0xff, 0xfe, 0xb0, 0x19, 0xff, 0xff, 0xf6, 0x37, 0x20, 0x78, 0xd7, 0xff, 0xff, 0x78, + 0x0e, 0xfc, 0x16, 0x44, 0xb7, 0x2b, 0x99, 0x7c, 0xcc, 0xe6, +}; +const uint8_t* const _I_Mode_hvr_25x27[] = {_I_Mode_hvr_25x27_0}; + +const uint8_t _I_Mute_25x27_0[] = { + 0x01, 0x00, 0x51, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x08, 0x24, 0x02, 0x81, 0x00, 0x81, 0x40, + 0x30, 0x10, 0x08, 0x08, 0x38, 0x60, 0x20, 0x31, 0x81, 0xc0, 0x64, 0x38, 0x08, 0xa4, 0x06, + 0x83, 0x40, 0x86, 0x40, 0x70, 0x32, 0x08, 0x20, 0x3c, 0x63, 0xf0, 0x60, 0x38, 0xc0, 0xa0, + 0xa0, 0x31, 0xc2, 0x02, 0xc7, 0x03, 0x48, 0x01, 0x94, 0xc0, 0x06, 0xc0, 0xb3, 0x09, 0x98, + 0x6c, 0x84, 0x68, 0x2b, 0x21, 0x99, 0x8e, 0xcc, 0x86, 0x64, 0xb3, 0x81, 0x94, 0xc6, 0x03, + 0x06, 0x80, 0x70, 0x20, 0x1f, 0xcf, 0xfd, 0xfc, 0xce, 0x80, +}; +const uint8_t* const _I_Mute_25x27[] = {_I_Mute_25x27_0}; + +const uint8_t _I_Mute_hvr_25x27_0[] = { + 0x01, 0x00, 0x4a, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x0f, 0xe7, 0xfe, 0xff, 0x00, 0xff, 0x7f, 0xff, + 0xf0, 0x00, 0x10, 0xff, 0xe0, 0x20, 0x21, 0xfe, 0x40, 0x7b, 0xf7, 0xff, 0x5c, 0x07, 0x7f, 0xbf, + 0xf9, 0xc0, 0x6f, 0xfd, 0xff, 0xd8, 0x3c, 0x7c, 0x1f, 0x90, 0x38, 0xff, 0x7f, 0x40, 0x31, 0xbd, + 0x82, 0xc6, 0xff, 0xb7, 0x01, 0x97, 0x3c, 0x06, 0xc0, 0xb3, 0x09, 0x98, 0x6c, 0x84, 0x68, 0x2b, + 0x21, 0x99, 0x8e, 0xcc, 0x86, 0x64, 0xb5, 0x01, 0x89, 0x5c, 0xcb, 0xe6, 0x67, 0x30, +}; +const uint8_t* const _I_Mute_hvr_25x27[] = {_I_Mute_hvr_25x27_0}; + +const uint8_t _I_Power_25x27_0[] = { + 0x01, 0x00, 0x54, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x08, 0x24, 0x02, 0x81, 0x00, 0x81, 0x40, + 0x30, 0x10, 0x08, 0x08, 0x38, 0x60, 0x30, 0x18, 0x80, 0x0c, 0xa7, 0x00, 0x35, 0xc0, 0xce, + 0x60, 0x70, 0x1e, 0x0c, 0xe6, 0x0f, 0x01, 0xf0, 0xce, 0x21, 0xd0, 0x1b, 0x0c, 0xe2, 0x18, + 0x03, 0x58, 0x80, 0x0c, 0xa0, 0x00, 0x39, 0xf0, 0xc0, 0x03, 0x63, 0xc1, 0x80, 0x88, 0xc7, + 0x03, 0x83, 0x15, 0x8c, 0x07, 0xfe, 0x02, 0x18, 0x0d, 0xf0, 0x76, 0x44, 0x73, 0x01, 0x94, + 0x0c, 0xa6, 0x30, 0x18, 0x34, 0x03, 0x81, 0x00, 0xfe, 0x7f, 0xef, 0xe6, 0x74, +}; +const uint8_t* const _I_Power_25x27[] = {_I_Power_25x27_0}; + +const uint8_t _I_Power_hvr_25x27_0[] = { + 0x01, 0x00, 0x4b, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x0f, 0xe7, 0xfe, 0xff, 0x00, 0xff, 0x7f, 0xff, + 0xf0, 0x00, 0x10, 0xff, 0xe0, 0x3f, 0xff, 0x78, 0x0c, 0xb8, 0xe0, 0x35, 0xbf, 0xf1, 0xbf, 0x90, + 0x19, 0xff, 0x1b, 0xf1, 0x01, 0x8f, 0xf1, 0xfe, 0x30, 0x1c, 0xff, 0x1f, 0xe6, 0x03, 0x5f, 0x78, + 0x0c, 0xbf, 0xe0, 0x39, 0x8f, 0xff, 0xc3, 0x63, 0x3f, 0xff, 0x08, 0xc6, 0xff, 0x7c, 0x15, 0x89, + 0x04, 0x7f, 0xc0, 0x31, 0xc1, 0x8e, 0xc8, 0x8e, 0x60, 0x36, 0x2b, 0x99, 0x7c, 0xcc, 0xe6, +}; +const uint8_t* const _I_Power_hvr_25x27[] = {_I_Power_hvr_25x27_0}; + +const uint8_t _I_Rotate_25x27_0[] = { + 0x01, 0x00, 0x4f, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x08, 0x24, 0x02, 0x81, 0x00, 0x81, + 0x40, 0x30, 0x10, 0x08, 0x08, 0x38, 0x60, 0x20, 0x37, 0xb0, 0xdc, 0x00, 0x63, 0xe1, + 0xfe, 0x02, 0x87, 0xc3, 0x8f, 0x81, 0x80, 0xc6, 0x0f, 0x07, 0x01, 0x94, 0x18, 0x06, + 0x30, 0xf8, 0x60, 0x0c, 0x63, 0xe0, 0x35, 0x11, 0x88, 0x0d, 0xc2, 0xe3, 0x81, 0x80, + 0x87, 0xc7, 0x03, 0x83, 0x13, 0x8c, 0x07, 0xfe, 0x02, 0x18, 0x0d, 0xf0, 0x87, 0x30, + 0x32, 0x98, 0xc0, 0x60, 0xd0, 0x0e, 0x04, 0x03, 0xf9, 0xff, 0xbf, 0x99, 0xd0, +}; +const uint8_t* const _I_Rotate_25x27[] = {_I_Rotate_25x27_0}; + +const uint8_t _I_Rotate_hvr_25x27_0[] = { + 0x01, 0x00, 0x48, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x0f, 0xe7, 0xfe, 0xff, 0x00, 0xff, 0x7f, 0xff, + 0xf0, 0x00, 0x10, 0xff, 0xe0, 0x20, 0x37, 0xcf, 0xe3, 0xc0, 0x63, 0x1f, 0x81, 0xff, 0x80, 0x63, + 0x70, 0xfe, 0x00, 0xc7, 0xf1, 0xf8, 0x01, 0x97, 0xe4, 0x06, 0x3f, 0x0f, 0x98, 0x0c, 0x7c, 0x00, + 0x35, 0x11, 0x08, 0x0d, 0xc2, 0xe3, 0x7f, 0xff, 0x07, 0xc6, 0xff, 0x7c, 0x13, 0x89, 0x04, 0x45, + 0xe3, 0xff, 0xc1, 0x90, 0xe4, 0x06, 0x65, 0x73, 0x2f, 0x99, 0x9c, 0xc0, +}; +const uint8_t* const _I_Rotate_hvr_25x27[] = {_I_Rotate_hvr_25x27_0}; + +const uint8_t _I_Swing_25x27_0[] = { + 0x01, 0x00, 0x5a, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x08, 0x24, 0x02, 0x81, 0x00, 0x81, 0x40, 0x30, + 0x10, 0x09, 0x0c, 0x02, 0x11, 0x01, 0x01, 0xa7, 0xfe, 0x12, 0x07, 0x4e, 0x0f, 0xfe, 0x0f, 0x01, + 0x80, 0x88, 0x84, 0x16, 0x39, 0x20, 0x58, 0xc4, 0x40, 0x03, 0x01, 0x84, 0xc4, 0x64, 0x30, 0x1c, + 0x2c, 0x42, 0x84, 0x13, 0x40, 0x1a, 0x48, 0x00, 0xeb, 0xb1, 0xd5, 0x4c, 0x60, 0x38, 0x9d, 0x55, + 0x2a, 0x03, 0x91, 0xd5, 0x42, 0xa0, 0x3a, 0x1d, 0x55, 0xaa, 0x03, 0x19, 0xd2, 0xcc, 0x82, 0xe9, + 0x03, 0x29, 0x8c, 0x06, 0x0d, 0x00, 0xe0, 0x40, 0x3f, 0x9f, 0xfb, 0xf9, 0x9d, 0x00, +}; +const uint8_t* const _I_Swing_25x27[] = {_I_Swing_25x27_0}; + +const uint8_t _I_Swing_hvr_25x27_0[] = { + 0x01, 0x00, 0x52, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x0f, 0xe7, 0xfe, 0xff, 0x00, 0xff, 0x7f, + 0xff, 0xf0, 0x0e, 0xff, 0xff, 0xef, 0x01, 0x01, 0xa4, 0x03, 0xec, 0x07, 0x40, 0xc8, 0xfe, + 0x20, 0x21, 0x61, 0xff, 0x82, 0xc6, 0xda, 0x0b, 0x1f, 0x7f, 0xbb, 0xdd, 0x01, 0xfb, 0xfb, + 0xbb, 0xd0, 0x1b, 0xdf, 0xbf, 0x7a, 0x13, 0x40, 0x1a, 0x09, 0x04, 0x0e, 0x94, 0xfa, 0xaf, + 0x3a, 0x03, 0x77, 0xaa, 0xed, 0x60, 0x36, 0xfa, 0xaf, 0xd6, 0x03, 0x5f, 0xaa, 0xe5, 0x60, + 0x3e, 0x7a, 0xd7, 0x34, 0x47, 0x05, 0x73, 0x2f, 0x99, 0x9c, 0xc0, +}; +const uint8_t* const _I_Swing_hvr_25x27[] = {_I_Swing_hvr_25x27_0}; + +const uint8_t _I_Timer_25x27_0[] = { + 0x01, 0x00, 0x60, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x08, 0x24, 0x02, 0x81, 0x00, 0x81, 0x5f, + 0x30, 0x10, 0x08, 0x0d, 0x10, 0x04, 0x30, 0x19, 0x44, 0x78, 0x06, 0x36, 0xc8, 0x90, 0x0c, + 0x72, 0x70, 0xd8, 0x0f, 0x06, 0x21, 0x06, 0x80, 0xd8, 0x62, 0x10, 0xc8, 0x0e, 0x86, 0x01, + 0x0a, 0x80, 0xc4, 0x60, 0x11, 0x00, 0x18, 0xc0, 0xc0, 0x63, 0x09, 0xc3, 0x48, 0x40, 0x63, + 0xcc, 0x90, 0x40, 0x6f, 0x37, 0x89, 0xe0, 0x1c, 0x66, 0x00, 0x71, 0x84, 0xc0, 0x00, 0x62, + 0x1b, 0x40, 0x19, 0x09, 0xcc, 0x5e, 0x63, 0x73, 0x1f, 0x88, 0x08, 0x60, 0x65, 0x31, 0x80, + 0xc1, 0xa0, 0x1c, 0x08, 0x07, 0xf3, 0xff, 0x7f, 0x33, 0xa0, +}; +const uint8_t* const _I_Timer_25x27[] = {_I_Timer_25x27_0}; + +const uint8_t _I_Timer_hvr_25x27_0[] = { + 0x01, 0x00, 0x5e, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x0f, 0xe7, 0xfe, 0xff, 0x00, 0xff, + 0x60, 0xff, 0xf0, 0x0f, 0xfe, 0xef, 0xff, 0x01, 0xff, 0xf5, 0xfc, 0x20, 0x31, 0xc9, + 0xfb, 0x40, 0x63, 0x6c, 0xf9, 0x40, 0x67, 0xfe, 0xff, 0xcc, 0x07, 0x3f, 0xef, 0xf9, + 0xc0, 0x6b, 0xff, 0xff, 0xac, 0x07, 0xdf, 0xff, 0xf7, 0x80, 0xc7, 0xf8, 0x03, 0x1f, + 0xbd, 0xe7, 0xbc, 0x03, 0x19, 0x9f, 0x7e, 0x03, 0x87, 0xc3, 0xf0, 0xc0, 0xe3, 0xcf, + 0x03, 0x8f, 0xdf, 0xfe, 0x03, 0x10, 0xda, 0x00, 0xc8, 0x4e, 0x62, 0xf3, 0x1b, 0x98, + 0xfc, 0x7f, 0xc0, 0x31, 0x29, 0x8c, 0x07, 0xfd, 0xff, 0x2b, 0x89, 0x7c, 0xcc, 0xe6, +}; +const uint8_t* const _I_Timer_hvr_25x27[] = {_I_Timer_hvr_25x27_0}; + +const uint8_t _I_Up_25x27_0[] = { + 0x01, 0x00, 0x44, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x08, 0x24, 0x02, 0x81, 0x00, 0x81, 0x40, + 0x30, 0x10, 0x08, 0x08, 0x38, 0x60, 0x20, 0x3c, 0x88, 0x00, 0xca, 0x70, 0x03, 0x2b, 0xe0, + 0x0c, 0xbf, 0xc0, 0x32, 0xff, 0x80, 0x87, 0x03, 0xff, 0x81, 0xc0, 0x78, 0x3f, 0xf8, 0x3c, + 0x07, 0xc3, 0xff, 0x87, 0xc0, 0x7e, 0x3f, 0xf8, 0xf8, 0x0d, 0x06, 0xfe, 0x03, 0x78, 0x19, + 0x4c, 0x60, 0x30, 0x68, 0x07, 0x02, 0x01, 0xfc, 0xff, 0xdf, 0xcc, 0xe8, +}; +const uint8_t* const _I_Up_25x27[] = {_I_Up_25x27_0}; + +const uint8_t _I_Up_hvr_25x27_0[] = { + 0x01, 0x00, 0x39, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x0f, 0xe7, 0xfe, 0xff, 0x00, 0xff, 0x7f, 0xff, + 0xf0, 0x00, 0x10, 0xff, 0xe0, 0x20, 0x3c, 0xf7, 0x80, 0xcb, 0x8e, 0x03, 0x2c, 0x18, 0x0c, 0x80, + 0x26, 0x25, 0x18, 0x08, 0xa4, 0x7f, 0x90, 0x11, 0x88, 0xfe, 0x20, 0x31, 0xf8, 0x07, 0xc2, 0x03, + 0x0f, 0x80, 0x78, 0x00, 0x68, 0x37, 0xf0, 0x1d, 0x95, 0xcc, 0xbe, 0x66, 0x73, +}; +const uint8_t* const _I_Up_hvr_25x27[] = {_I_Up_hvr_25x27_0}; + +const uint8_t _I_Vol_down_25x27_0[] = { + 0x01, 0x00, 0x2c, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x08, 0x24, 0x02, 0x81, 0x00, 0x81, 0x40, 0x30, + 0x10, 0x08, 0x08, 0x38, 0x60, 0x20, 0x3f, 0x01, 0xff, 0x07, 0xff, 0x07, 0x01, 0xa0, 0x5f, 0xc0, + 0x7e, 0x03, 0x38, 0x19, 0x4c, 0x60, 0x30, 0x68, 0x07, 0x02, 0x01, 0xfc, 0xff, 0xdf, 0xcc, 0xe8, +}; +const uint8_t* const _I_Vol_down_25x27[] = {_I_Vol_down_25x27_0}; + +const uint8_t _I_Vol_down_hvr_25x27_0[] = { + 0x01, 0x00, 0x23, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x0f, 0xe7, 0xfe, 0xff, 0x00, + 0xff, 0x7f, 0xff, 0xf0, 0x00, 0x10, 0xff, 0xe0, 0x20, 0x3f, 0x01, 0xf8, 0xb4, + 0x7f, 0x00, 0x34, 0x0b, 0xf8, 0x0f, 0xc0, 0x6e, 0x57, 0x32, 0xf9, 0x99, 0xcc, +}; +const uint8_t* const _I_Vol_down_hvr_25x27[] = {_I_Vol_down_hvr_25x27_0}; + +const uint8_t _I_Vol_up_25x27_0[] = { + 0x01, 0x00, 0x2f, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x08, 0x24, 0x02, 0x81, 0x00, + 0x81, 0x40, 0x30, 0x10, 0x08, 0x08, 0x38, 0x60, 0x20, 0x38, 0x88, 0x00, 0xfc, + 0x06, 0xbc, 0x1f, 0xfc, 0x1c, 0x06, 0x81, 0x7f, 0x01, 0xc1, 0x0e, 0xa0, 0x65, + 0x31, 0x80, 0xc1, 0xa0, 0x1c, 0x08, 0x07, 0xf3, 0xff, 0x7f, 0x33, 0xa0, +}; +const uint8_t* const _I_Vol_up_25x27[] = {_I_Vol_up_25x27_0}; + +const uint8_t _I_Vol_up_hvr_25x27_0[] = { + 0x01, 0x00, 0x28, 0x00, 0xfc, 0x7f, 0xe7, 0xf0, 0x0f, 0xe7, 0xfe, 0xff, 0x00, 0xff, 0x7f, + 0xff, 0xf0, 0x00, 0x10, 0xff, 0xe0, 0x20, 0x38, 0xf7, 0x80, 0xfc, 0x06, 0xa2, 0xd1, 0xfc, + 0x00, 0xd0, 0x2f, 0xe0, 0x38, 0x21, 0xd8, 0x0c, 0x8a, 0xe6, 0x5f, 0x33, 0x39, 0x80, +}; +const uint8_t* const _I_Vol_up_hvr_25x27[] = {_I_Vol_up_hvr_25x27_0}; + +const uint8_t _I_DoorLeft_70x55_0[] = { + 0x01, 0x00, 0x19, 0x01, 0x00, 0x2c, 0x32, 0x01, 0x03, 0x04, 0x2c, 0x18, 0x10, 0xf0, 0x40, 0x47, + 0x82, 0x06, 0x81, 0x03, 0xff, 0x80, 0x08, 0x1a, 0x20, 0x82, 0x15, 0x28, 0x21, 0x87, 0x82, 0x08, + 0x6f, 0xc0, 0xb1, 0xe6, 0x10, 0x10, 0x8b, 0x46, 0x20, 0x43, 0x55, 0x8f, 0x82, 0x10, 0x32, 0x73, + 0x0a, 0x09, 0x89, 0x6c, 0x1e, 0x09, 0x00, 0x18, 0x60, 0xf0, 0x0c, 0x84, 0x93, 0x82, 0x03, 0x18, + 0x0c, 0x02, 0x1d, 0x00, 0x90, 0x52, 0x70, 0x50, 0x1e, 0x00, 0x58, 0x63, 0x90, 0x0a, 0x06, 0x4a, + 0x09, 0x03, 0xb0, 0x02, 0x06, 0x70, 0x62, 0x49, 0xf8, 0x0c, 0x66, 0x3f, 0xf0, 0x41, 0x63, 0x04, + 0x43, 0x00, 0x99, 0x60, 0x00, 0x85, 0xc8, 0x06, 0x14, 0xd0, 0x80, 0x3f, 0xc8, 0x0d, 0xb8, 0x10, + 0x70, 0xf8, 0x34, 0x13, 0x03, 0x39, 0x04, 0x1c, 0x42, 0x19, 0xf8, 0xa0, 0xc2, 0x01, 0x07, 0xef, + 0x02, 0x8c, 0x80, 0x10, 0x9d, 0x00, 0x43, 0xec, 0x00, 0xa3, 0x10, 0x04, 0x25, 0xce, 0x19, 0xfc, + 0x88, 0x82, 0x12, 0x0c, 0x35, 0x10, 0x42, 0x4c, 0xa1, 0x90, 0x3f, 0xc0, 0x21, 0x22, 0x39, 0x82, + 0xc8, 0x88, 0xd2, 0x11, 0xf0, 0x01, 0x88, 0xd5, 0x18, 0xe2, 0x08, 0x68, 0x10, 0x0c, 0xa8, 0x00, + 0x83, 0x81, 0xcc, 0xd5, 0xc3, 0x80, 0x84, 0x82, 0x0e, 0xcc, 0xc0, 0x15, 0x79, 0x02, 0x0b, 0x98, + 0xf8, 0x11, 0x88, 0x82, 0x0f, 0x31, 0x19, 0x02, 0x08, 0x2c, 0x9f, 0x6a, 0x1d, 0x20, 0x41, 0x31, + 0x4c, 0x10, 0x8d, 0x73, 0x04, 0x23, 0xa4, 0xc4, 0x6c, 0xde, 0x20, 0x42, 0xcc, 0x01, 0x07, 0x07, + 0xff, 0x80, 0x06, 0x3e, 0x08, 0x38, 0x70, 0x20, 0xa1, 0xe0, 0x83, 0x8e, 0x01, 0x0c, 0xf0, 0x73, + 0x80, 0x43, 0x70, 0x05, 0x08, 0x00, 0x2c, 0x04, 0xc4, 0x46, 0x53, 0x09, 0x98, 0x24, 0x80, 0x65, + 0x80, 0xb0, 0xd9, 0x84, 0x65, 0x32, 0x06, 0x17, 0x0f, 0x98, 0x23, 0x63, 0xe1, 0x88, 0xc4, 0x08, + 0x5f, 0xc1, 0x30, 0x9d, 0x84, 0x4e, 0x66, 0x94, 0x11, 0x98, 0x75, 0x26, 0x00, +}; +const uint8_t* const _I_DoorLeft_70x55[] = {_I_DoorLeft_70x55_0}; + +const uint8_t _I_DoorRight_70x55_0[] = { + 0x01, 0x00, 0x16, 0x01, 0x81, 0xcc, 0x01, 0x0f, 0x60, 0x04, 0x3f, 0x00, 0x10, 0xf8, 0x08, 0x0c, + 0x02, 0x05, 0x01, 0x84, 0x02, 0x06, 0x26, 0x0a, 0x10, 0x8a, 0xcc, 0xe0, 0x1d, 0x68, 0xe0, 0x18, + 0xab, 0xd0, 0x0b, 0x18, 0x10, 0x46, 0xe6, 0x16, 0x1e, 0x18, 0x10, 0x46, 0xe4, 0x28, 0x2c, 0x98, + 0x14, 0x68, 0x00, 0x21, 0x1d, 0x10, 0x8c, 0x40, 0x02, 0x0e, 0x10, 0xa1, 0x08, 0xc8, 0x40, 0x42, + 0x62, 0x11, 0x94, 0x03, 0xfd, 0xff, 0x00, 0x0c, 0xff, 0x0c, 0x08, 0x28, 0x60, 0xe4, 0xc0, 0x85, + 0x00, 0x83, 0x00, 0x87, 0xf1, 0x00, 0x8c, 0x02, 0x0b, 0x07, 0x24, 0x84, 0xff, 0x04, 0xc7, 0x80, + 0xa0, 0xe4, 0xa0, 0x81, 0x41, 0x04, 0x17, 0x02, 0x41, 0x49, 0x81, 0x0e, 0x10, 0xb2, 0xa0, 0x82, + 0x0e, 0x9f, 0xfc, 0x0a, 0x62, 0xf2, 0xc0, 0x03, 0x92, 0xf0, 0x08, 0x2d, 0x78, 0x20, 0xff, 0x02, + 0x01, 0x08, 0xae, 0x60, 0x64, 0x38, 0x0d, 0xb0, 0x8d, 0x08, 0x82, 0x11, 0x58, 0xc4, 0x13, 0xc0, + 0x35, 0x68, 0x62, 0x68, 0x81, 0x09, 0x08, 0x84, 0x40, 0x81, 0x0d, 0x18, 0x69, 0x10, 0x47, 0x44, + 0x66, 0x5f, 0x21, 0xa9, 0x29, 0x94, 0x10, 0x2f, 0x23, 0x53, 0x14, 0x60, 0x42, 0x3c, 0x08, 0xfc, + 0x02, 0x2c, 0x62, 0x23, 0x58, 0xd0, 0x22, 0x00, 0x83, 0x3e, 0x98, 0x44, 0x43, 0x46, 0x22, 0x30, + 0x89, 0xce, 0x01, 0x0f, 0x70, 0x04, 0x3f, 0x81, 0x8a, 0x3c, 0x21, 0xaa, 0x70, 0x1a, 0xe3, 0x44, + 0x1a, 0xa6, 0x01, 0xd2, 0x38, 0x90, 0x8a, 0x40, 0x20, 0xe5, 0x96, 0x80, 0x43, 0x81, 0x06, 0x6b, + 0x28, 0x07, 0xf3, 0xfe, 0x00, 0x19, 0xf9, 0x34, 0xc1, 0x08, 0x8f, 0x20, 0xf1, 0x3e, 0x16, 0x00, + 0xa8, 0x19, 0x00, 0x10, 0x76, 0x03, 0xe2, 0x3e, 0x90, 0x45, 0x38, 0x01, 0x42, 0x05, 0x88, 0x44, + 0x67, 0x15, 0x70, 0x41, 0x38, 0x04, 0x10, 0x24, 0x03, 0x00, 0x10, 0x20, 0x4a, 0x46, 0xe9, 0x46, + 0xe1, 0x04, 0x50, 0x66, 0x40, 0x85, 0x19, 0x98, 0x00, 0xc0, +}; +const uint8_t* const _I_DoorRight_70x55[] = {_I_DoorRight_70x55_0}; + +const uint8_t _I_SmallArrowDown_3x5_0[] = { + 0x00, + 0x1f, + 0x0e, + 0x04, +}; +const uint8_t* const _I_SmallArrowDown_3x5[] = {_I_SmallArrowDown_3x5_0}; + +const uint8_t _I_SmallArrowDown_4x7_0[] = { + 0x00, + 0x7f, + 0x3e, + 0x1c, + 0x08, +}; +const uint8_t* const _I_SmallArrowDown_4x7[] = {_I_SmallArrowDown_4x7_0}; + +const uint8_t _I_SmallArrowUp_3x5_0[] = { + 0x00, + 0x04, + 0x0e, + 0x1f, +}; +const uint8_t* const _I_SmallArrowUp_3x5[] = {_I_SmallArrowUp_3x5_0}; + +const uint8_t _I_SmallArrowUp_4x7_0[] = { + 0x00, + 0x08, + 0x1c, + 0x3e, + 0x7f, +}; +const uint8_t* const _I_SmallArrowUp_4x7[] = {_I_SmallArrowUp_4x7_0}; + +const uint8_t _I_WarningDolphin_45x42_0[] = { + 0x01, 0x00, 0xc6, 0x00, 0x00, 0x1c, 0x22, 0x04, 0x05, 0x7f, 0xfc, 0x1e, 0x20, 0x05, 0x1e, 0x04, + 0x02, 0x30, 0x05, 0x29, 0x84, 0x02, 0xc1, 0x20, 0x02, 0x8c, 0x22, 0x01, 0x80, 0x02, 0x94, 0x10, + 0x32, 0x30, 0x10, 0x10, 0x87, 0xca, 0x84, 0x03, 0x10, 0x42, 0x81, 0x48, 0x28, 0x38, 0x08, 0x04, + 0x3e, 0x01, 0x84, 0x83, 0xe0, 0x30, 0x11, 0x08, 0x05, 0xa2, 0x11, 0x40, 0xa0, 0x4b, 0xc6, 0xc5, + 0x40, 0xd0, 0x56, 0xe0, 0x10, 0x60, 0x29, 0x54, 0xf0, 0x10, 0x18, 0xf0, 0x14, 0x6b, 0xf6, 0x0c, + 0x04, 0x3e, 0x40, 0x05, 0x12, 0x80, 0xc1, 0xe4, 0x01, 0xd2, 0xf8, 0x40, 0xe4, 0x18, 0x09, 0xf4, + 0x03, 0xf1, 0x01, 0x90, 0x40, 0x28, 0x30, 0x0f, 0xe4, 0x00, 0x16, 0x24, 0x11, 0xbf, 0x01, 0x44, + 0xee, 0x53, 0xf0, 0x29, 0xf0, 0x3e, 0x02, 0x91, 0x3b, 0x8c, 0xc3, 0x81, 0x13, 0x90, 0x48, 0x20, + 0x3f, 0xf9, 0xfc, 0x42, 0x60, 0x05, 0x10, 0x98, 0x81, 0x56, 0x11, 0x38, 0x02, 0x9c, 0x1a, 0x31, + 0x1e, 0x02, 0x8f, 0x02, 0x03, 0x1c, 0x90, 0xc0, 0x7c, 0x02, 0xf1, 0xce, 0x02, 0x07, 0x01, 0x1f, + 0x80, 0x63, 0xa8, 0x08, 0x71, 0x3c, 0x8e, 0x39, 0x24, 0x40, 0x51, 0xc7, 0x81, 0x53, 0x0f, 0x3c, + 0x02, 0x9d, 0x1e, 0x38, 0x29, 0x10, 0x29, 0x17, 0xc8, 0x0a, 0x32, 0x3a, 0x00, 0x14, 0x4b, 0xa2, + 0x05, 0x58, 0x98, 0x15, 0x22, 0x20, 0x54, 0x84, 0x81, 0x50, +}; +const uint8_t* const _I_WarningDolphin_45x42[] = {_I_WarningDolphin_45x42_0}; + +const uint8_t _I_KeyBackspaceSelected_16x9_0[] = { + 0x00, 0xfe, 0x7f, 0xff, 0xff, 0xef, 0xff, 0xe7, 0xff, 0x03, + 0xc0, 0xe7, 0xff, 0xef, 0xff, 0xff, 0xff, 0xfe, 0x7f, +}; +const uint8_t* const _I_KeyBackspaceSelected_16x9[] = {_I_KeyBackspaceSelected_16x9_0}; + +const uint8_t _I_KeyBackspace_16x9_0[] = { + 0x00, 0xfe, 0x7f, 0x01, 0x80, 0x11, 0x80, 0x19, 0x80, 0xfd, + 0xbf, 0x19, 0x80, 0x11, 0x80, 0x01, 0x80, 0xfe, 0x7f, +}; +const uint8_t* const _I_KeyBackspace_16x9[] = {_I_KeyBackspace_16x9_0}; + +const uint8_t _I_KeySaveSelected_24x11_0[] = { + 0x01, 0x00, 0x1a, 0x00, 0xff, 0x7f, 0xc0, 0x0d, 0xcf, 0xb4, 0x7c, 0xee, 0xf6, 0xbf, 0x6d, + 0xbe, 0xd7, 0xe1, 0xaf, 0xda, 0xff, 0xbe, 0x7c, 0xc7, 0xcc, 0x28, 0xa1, 0xd1, 0xbf, 0x80, +}; +const uint8_t* const _I_KeySaveSelected_24x11[] = {_I_KeySaveSelected_24x11_0}; + +const uint8_t _I_KeySave_24x11_0[] = { + 0x01, 0x00, 0x1e, 0x00, 0xff, 0x7f, 0xff, 0xf0, 0x18, 0x06, 0x00, 0x04, + 0x53, 0x1c, 0xbe, 0x33, 0x13, 0x94, 0xc9, 0x64, 0x72, 0x99, 0xed, 0x0e, + 0x53, 0x05, 0x19, 0xb3, 0xe3, 0x02, 0x8a, 0x1d, 0x1b, 0xf8, +}; +const uint8_t* const _I_KeySave_24x11[] = {_I_KeySave_24x11_0}; + +const uint8_t _A_125khz_14_0[] = { + 0x00, 0x80, 0x07, 0x00, 0x08, 0x00, 0x13, 0x00, 0x24, 0x0e, 0x28, 0x71, 0x28, 0x85, 0x21, + 0x01, 0x02, 0x62, 0x02, 0x92, 0x02, 0x92, 0x02, 0x64, 0x02, 0x04, 0x01, 0xf8, 0x00, +}; +const uint8_t _A_125khz_14_1[] = { + 0x00, 0x80, 0x07, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x0e, 0x20, 0x71, 0x20, 0x85, 0x21, + 0x01, 0x02, 0x62, 0x02, 0x92, 0x02, 0x92, 0x02, 0x64, 0x02, 0x04, 0x01, 0xf8, 0x00, +}; +const uint8_t _A_125khz_14_2[] = { + 0x01, 0x00, 0x17, 0x00, 0x00, 0x3c, 0x3a, 0x01, 0x71, 0x80, 0x61, 0x60, 0x30, 0x18, + 0x15, 0x8a, 0x05, 0x92, 0x00, 0x95, 0x92, 0x05, 0x04, 0x80, 0xfe, 0x20, 0x00, +}; +const uint8_t _A_125khz_14_3[] = { + 0x01, 0x00, 0x1a, 0x00, 0x00, 0x24, 0x0e, 0x01, 0x04, 0x87, 0x42, 0x2e, 0x30, 0x8c, 0x2c, + 0x06, 0x03, 0x02, 0xb1, 0x40, 0xb2, 0x40, 0x12, 0xb2, 0x40, 0xa0, 0x90, 0x1f, 0xc4, 0x00, +}; +const uint8_t* const _A_125khz_14[] = + {_A_125khz_14_0, _A_125khz_14_1, _A_125khz_14_2, _A_125khz_14_3}; + +const uint8_t _A_BadUsb_14_0[] = { + 0x00, 0xff, 0x1f, 0x01, 0x10, 0x01, 0x10, 0xf1, 0x11, 0xf9, 0x13, 0xe9, 0x12, 0x49, 0x12, + 0xf9, 0x13, 0xf1, 0x11, 0x51, 0x11, 0x01, 0x10, 0xff, 0x1f, 0xfe, 0x0f, 0xfe, 0x0f, +}; +const uint8_t _A_BadUsb_14_1[] = { + 0x00, 0x01, 0x10, 0x01, 0x10, 0xe1, 0x13, 0xfd, 0x10, 0xf9, 0x13, 0x01, 0x17, 0xf9, 0x13, + 0x3d, 0x10, 0xf1, 0x11, 0x01, 0x10, 0xff, 0x1f, 0x06, 0x0c, 0xfe, 0x0f, 0xfe, 0x0f, +}; +const uint8_t _A_BadUsb_14_2[] = { + 0x00, 0x01, 0x10, 0xf1, 0x11, 0xf9, 0x13, 0x59, 0x13, 0xf9, 0x13, 0xe9, 0x12, 0x19, 0x13, + 0xf1, 0x11, 0x01, 0x10, 0xff, 0x1f, 0x04, 0x04, 0xb6, 0x0d, 0xfe, 0x0f, 0xfe, 0x0f, +}; +const uint8_t _A_BadUsb_14_3[] = { + 0x00, 0xf1, 0x11, 0xf9, 0x13, 0x59, 0x13, 0xf9, 0x13, 0xe9, 0x12, 0x19, 0x13, 0xf1, 0x11, + 0x01, 0x10, 0xff, 0x1f, 0x04, 0x04, 0xb4, 0x05, 0x06, 0x0c, 0xfe, 0x0f, 0xfe, 0x0f, +}; +const uint8_t _A_BadUsb_14_4[] = { + 0x00, 0xf9, 0x13, 0xe9, 0x12, 0x19, 0x13, 0xf1, 0x11, 0x01, 0x10, 0xff, 0x1f, 0x04, 0x04, + 0xb4, 0x05, 0x04, 0x04, 0xfc, 0x07, 0xfc, 0x07, 0xfe, 0x0f, 0xfe, 0x0f, 0x02, 0x08, +}; +const uint8_t _A_BadUsb_14_5[] = { + 0x00, 0xf1, 0x11, 0x01, 0x10, 0xff, 0x1f, 0x04, 0x04, 0xb4, 0x05, 0x04, 0x04, 0xfc, 0x07, + 0xfc, 0x07, 0x04, 0x04, 0xfc, 0x07, 0x00, 0x00, 0xfe, 0x0f, 0xfe, 0x0f, 0x02, 0x08, +}; +const uint8_t _A_BadUsb_14_6[] = { + 0x00, 0xf9, 0x13, 0xe9, 0x12, 0x19, 0x13, 0xf1, 0x11, 0x01, 0x10, 0xff, 0x1f, 0x04, 0x04, + 0xb4, 0x05, 0x04, 0x04, 0xfc, 0x07, 0xfc, 0x07, 0xfe, 0x0f, 0xfe, 0x0f, 0x02, 0x08, +}; +const uint8_t _A_BadUsb_14_7[] = { + 0x00, 0xf1, 0x11, 0xf9, 0x13, 0x59, 0x13, 0xf9, 0x13, 0xe9, 0x12, 0x19, 0x13, 0xf1, 0x11, + 0x01, 0x10, 0xff, 0x1f, 0x04, 0x04, 0xb4, 0x05, 0x06, 0x0c, 0xfe, 0x0f, 0xfe, 0x0f, +}; +const uint8_t _A_BadUsb_14_8[] = { + 0x00, 0x01, 0x10, 0xf1, 0x11, 0xf9, 0x13, 0x59, 0x13, 0xf9, 0x13, 0xe9, 0x12, 0x19, 0x13, + 0xf1, 0x11, 0x01, 0x10, 0xff, 0x1f, 0x04, 0x04, 0xb6, 0x0d, 0xfe, 0x0f, 0xfe, 0x0f, +}; +const uint8_t _A_BadUsb_14_9[] = { + 0x00, 0x01, 0x10, 0x01, 0x10, 0xe1, 0x13, 0xfd, 0x10, 0xf9, 0x13, 0x01, 0x17, 0xf9, 0x13, + 0x3d, 0x10, 0xf1, 0x11, 0x01, 0x10, 0xff, 0x1f, 0x06, 0x0c, 0xfe, 0x0f, 0xfe, 0x0f, +}; +const uint8_t _A_BadUsb_14_10[] = { + 0x00, 0xff, 0x1f, 0x01, 0x10, 0x01, 0x10, 0xf1, 0x11, 0xf9, 0x13, 0xe9, 0x12, 0x49, 0x12, + 0xf9, 0x13, 0xf1, 0x11, 0x51, 0x11, 0x01, 0x10, 0xff, 0x1f, 0xfe, 0x0f, 0xfe, 0x0f, +}; +const uint8_t* const _A_BadUsb_14[] = { + _A_BadUsb_14_0, + _A_BadUsb_14_1, + _A_BadUsb_14_2, + _A_BadUsb_14_3, + _A_BadUsb_14_4, + _A_BadUsb_14_5, + _A_BadUsb_14_6, + _A_BadUsb_14_7, + _A_BadUsb_14_8, + _A_BadUsb_14_9, + _A_BadUsb_14_10}; + +const uint8_t _A_Clock_14_0[] = { + 0x00, 0xf0, 0x01, 0x0e, 0x0e, 0x47, 0x1c, 0x03, 0x18, 0x49, 0x12, 0x41, 0x10, 0x41, 0x10, + 0x01, 0x10, 0x09, 0x12, 0x02, 0x08, 0x46, 0x0c, 0x0e, 0x0e, 0xf6, 0x0d, 0x02, 0x08, +}; +const uint8_t _A_Clock_14_1[] = { + 0x00, 0xf0, 0x01, 0x0e, 0x0e, 0x47, 0x1c, 0x03, 0x18, 0x09, 0x13, 0x81, 0x10, 0x41, 0x10, + 0x01, 0x10, 0x09, 0x12, 0x02, 0x08, 0x46, 0x0c, 0x0e, 0x0e, 0xf6, 0x0d, 0x02, 0x08, +}; +const uint8_t _A_Clock_14_2[] = { + 0x00, 0xf0, 0x01, 0x0e, 0x0e, 0x47, 0x1c, 0x03, 0x18, 0x09, 0x12, 0x01, 0x10, 0xc1, 0x11, + 0x01, 0x10, 0x09, 0x12, 0x02, 0x08, 0x46, 0x0c, 0x0e, 0x0e, 0xf6, 0x0d, 0x02, 0x08, +}; +const uint8_t _A_Clock_14_3[] = { + 0x00, 0xf0, 0x01, 0x0e, 0x0e, 0x47, 0x1c, 0x03, 0x18, 0x09, 0x12, 0x01, 0x10, 0x41, 0x10, + 0x81, 0x10, 0x09, 0x13, 0x02, 0x08, 0x46, 0x0c, 0x0e, 0x0e, 0xf6, 0x0d, 0x02, 0x08, +}; +const uint8_t _A_Clock_14_4[] = { + 0x00, 0xf0, 0x01, 0x0e, 0x0e, 0x47, 0x1c, 0x03, 0x18, 0x09, 0x12, 0x01, 0x10, 0x41, 0x10, + 0x41, 0x10, 0x49, 0x12, 0x02, 0x08, 0x46, 0x0c, 0x0e, 0x0e, 0xf6, 0x0d, 0x02, 0x08, +}; +const uint8_t _A_Clock_14_5[] = { + 0x00, 0xf0, 0x01, 0x0e, 0x0e, 0x47, 0x1c, 0x03, 0x18, 0x09, 0x12, 0x01, 0x10, 0x41, 0x10, + 0x21, 0x10, 0x19, 0x12, 0x02, 0x08, 0x46, 0x0c, 0x0e, 0x0e, 0xf6, 0x0d, 0x02, 0x08, +}; +const uint8_t _A_Clock_14_6[] = { + 0x00, 0xf0, 0x01, 0x0e, 0x0e, 0x47, 0x1c, 0x03, 0x18, 0x09, 0x12, 0x01, 0x10, 0x71, 0x10, + 0x01, 0x10, 0x09, 0x12, 0x02, 0x08, 0x46, 0x0c, 0x0e, 0x0e, 0xf6, 0x0d, 0x02, 0x08, +}; +const uint8_t _A_Clock_14_7[] = { + 0x00, 0xf0, 0x01, 0x0e, 0x0e, 0x47, 0x1c, 0x03, 0x18, 0x19, 0x12, 0x21, 0x10, 0x41, 0x10, + 0x01, 0x10, 0x09, 0x12, 0x02, 0x08, 0x46, 0x0c, 0x0e, 0x0e, 0xf6, 0x0d, 0x02, 0x08, +}; +const uint8_t* const _A_Clock_14[] = { + _A_Clock_14_0, + _A_Clock_14_1, + _A_Clock_14_2, + _A_Clock_14_3, + _A_Clock_14_4, + _A_Clock_14_5, + _A_Clock_14_6, + _A_Clock_14_7}; + +const uint8_t _A_Debug_14_0[] = { + 0x00, 0x20, 0x01, 0xc1, 0x20, 0x22, 0x11, 0x24, 0x09, 0xd9, 0x26, 0x16, 0x1a, 0xd8, 0x06, + 0xd8, 0x06, 0xd6, 0x1a, 0x19, 0x26, 0xe4, 0x09, 0xc2, 0x10, 0x01, 0x20, 0x00, 0x00, +}; +const uint8_t _A_Debug_14_1[] = { + 0x00, 0x20, 0x01, 0xc0, 0x00, 0x22, 0x11, 0x25, 0x29, 0xd8, 0x06, 0x16, 0x1a, 0xd9, 0x26, + 0xd8, 0x06, 0xd4, 0x0a, 0x12, 0x12, 0xea, 0x15, 0xc5, 0x28, 0x02, 0x10, 0x02, 0x10, +}; +const uint8_t _A_Debug_14_2[] = { + 0x00, 0x20, 0x01, 0xc0, 0x00, 0x20, 0x01, 0x24, 0x09, 0xda, 0x16, 0x11, 0x22, 0xdc, 0x0e, + 0xda, 0x16, 0xd9, 0x26, 0x14, 0x0a, 0xf2, 0x13, 0xd1, 0x22, 0x08, 0x04, 0x06, 0x18, +}; +const uint8_t _A_Debug_14_3[] = { + 0x00, 0x22, 0x11, 0xc4, 0x08, 0x24, 0x09, 0x25, 0x29, 0xd9, 0x26, 0x12, 0x12, 0xdc, 0x0e, + 0xd8, 0x06, 0xd8, 0x06, 0x14, 0x0a, 0xf4, 0x0b, 0xd2, 0x12, 0x19, 0x26, 0x06, 0x18, +}; +const uint8_t* const _A_Debug_14[] = {_A_Debug_14_0, _A_Debug_14_1, _A_Debug_14_2, _A_Debug_14_3}; + +const uint8_t _A_FileManager_14_0[] = { + 0x00, 0xfc, 0x07, 0x04, 0x04, 0xf4, 0x05, 0x04, 0x04, 0xf7, 0x05, 0x05, 0x04, 0xf5, 0x3f, + 0x15, 0x20, 0x0d, 0x20, 0x0d, 0x10, 0x05, 0x10, 0x05, 0x08, 0x03, 0x08, 0xfe, 0x07, +}; +const uint8_t _A_FileManager_14_1[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0x04, 0x04, 0xf7, 0x05, 0x05, 0x04, 0xf5, 0x3f, + 0x15, 0x20, 0x0d, 0x20, 0x0d, 0x10, 0x05, 0x10, 0x05, 0x08, 0x03, 0x08, 0xfe, 0x07, +}; +const uint8_t _A_FileManager_14_2[] = { + 0x01, 0x00, 0x17, 0x00, 0x00, 0x3f, 0xfe, 0x0f, 0x05, 0x82, 0x7d, 0x67, 0xf1, 0x59, + 0x04, 0x34, 0x02, 0x31, 0x08, 0x28, 0x04, 0x61, 0x10, 0x38, 0x47, 0xfa, 0x0e, +}; +const uint8_t _A_FileManager_14_3[] = { + 0x01, 0x00, 0x17, 0x00, 0x00, 0x3c, 0x3e, 0x01, 0x11, 0x80, 0x7f, 0x67, 0xf1, 0x59, + 0x04, 0x34, 0x02, 0x31, 0x08, 0x28, 0x04, 0x61, 0x10, 0x38, 0x47, 0xfa, 0x0e, +}; +const uint8_t _A_FileManager_14_4[] = { + 0x01, 0x00, 0x17, 0x00, 0x00, 0x3c, 0x3e, 0x01, 0x11, 0x80, 0x7c, 0x67, 0xf1, 0x19, + 0x04, 0x34, 0x02, 0x31, 0x08, 0x28, 0x04, 0x61, 0x10, 0x38, 0x47, 0xfa, 0x0e, +}; +const uint8_t _A_FileManager_14_5[] = { + 0x01, 0x00, 0x0f, 0x00, 0x00, 0x3c, 0x3e, 0x01, 0x11, 0x80, + 0x7f, 0xe0, 0xf0, 0x18, 0x40, 0x06, 0x7f, 0xd0, 0x70, +}; +const uint8_t _A_FileManager_14_6[] = { + 0x01, 0x00, 0x17, 0x00, 0x00, 0x3c, 0x3e, 0x01, 0x11, 0x80, 0x7c, 0x67, 0xf1, 0x19, + 0x04, 0x34, 0x02, 0x31, 0x08, 0x28, 0x04, 0x61, 0x10, 0x38, 0x47, 0xfa, 0x0e, +}; +const uint8_t _A_FileManager_14_7[] = { + 0x01, 0x00, 0x17, 0x00, 0x00, 0x3c, 0x3e, 0x01, 0x11, 0x80, 0x7f, 0x67, 0xf1, 0x59, + 0x04, 0x34, 0x02, 0x31, 0x08, 0x28, 0x04, 0x61, 0x10, 0x38, 0x47, 0xfa, 0x0e, +}; +const uint8_t _A_FileManager_14_8[] = { + 0x01, 0x00, 0x17, 0x00, 0x00, 0x3f, 0xfe, 0x0f, 0x05, 0x82, 0x7d, 0x67, 0xf1, 0x59, + 0x04, 0x34, 0x02, 0x31, 0x08, 0x28, 0x04, 0x61, 0x10, 0x38, 0x47, 0xfa, 0x0e, +}; +const uint8_t _A_FileManager_14_9[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0x04, 0x04, 0xf7, 0x05, 0x05, 0x04, 0xf5, 0x3f, + 0x15, 0x20, 0x0d, 0x20, 0x0d, 0x10, 0x05, 0x10, 0x05, 0x08, 0x03, 0x08, 0xfe, 0x07, +}; +const uint8_t* const _A_FileManager_14[] = { + _A_FileManager_14_0, + _A_FileManager_14_1, + _A_FileManager_14_2, + _A_FileManager_14_3, + _A_FileManager_14_4, + _A_FileManager_14_5, + _A_FileManager_14_6, + _A_FileManager_14_7, + _A_FileManager_14_8, + _A_FileManager_14_9}; + +const uint8_t _A_GPIO_14_0[] = { + 0x01, 0x00, 0x15, 0x00, 0xa2, 0x41, 0x00, 0x23, 0xee, 0x87, 0x00, 0x54, 0x16, + 0x60, 0x11, 0x09, 0x8f, 0xfe, 0x3f, 0x11, 0x88, 0xd5, 0x62, 0xa0, 0x31, +}; +const uint8_t _A_GPIO_14_1[] = { + 0x01, 0x00, 0x12, 0x00, 0xa2, 0x41, 0x00, 0x27, 0xee, 0x87, 0x00, + 0x54, 0x1a, 0xbf, 0xf8, 0xfc, 0x46, 0x23, 0x55, 0x8a, 0x80, 0xc4, +}; +const uint8_t _A_GPIO_14_2[] = { + 0x01, 0x00, 0x12, 0x00, 0xa2, 0x41, 0x00, 0x2f, 0xee, 0x87, 0x00, + 0x54, 0x12, 0x3f, 0xf8, 0xfd, 0x56, 0x2a, 0x01, 0x18, 0x8c, 0x44, +}; +const uint8_t _A_GPIO_14_3[] = { + 0x01, 0x00, 0x11, 0x00, 0xa2, 0x41, 0x00, 0x37, 0xee, 0x87, 0x00, + 0x4f, 0xff, 0x1f, 0xaa, 0xc5, 0x40, 0x23, 0x11, 0x88, 0x80, +}; +const uint8_t _A_GPIO_14_4[] = { + 0x01, + 0x00, + 0x0d, + 0x00, + 0xa2, + 0x41, + 0x00, + 0x3f, + 0xee, + 0x87, + 0x7f, + 0xe3, + 0xe0, + 0x13, + 0x88, + 0xc4, + 0x40, +}; +const uint8_t _A_GPIO_14_5[] = { + 0x01, 0x00, 0x11, 0x00, 0xa2, 0x41, 0x00, 0x3b, 0xee, 0x87, 0x00, + 0x47, 0xff, 0x1f, 0x00, 0x8d, 0x56, 0x2b, 0x11, 0x88, 0x80, +}; +const uint8_t _A_GPIO_14_6[] = { + 0x01, 0x00, 0x11, 0x00, 0xa2, 0x41, 0x00, 0x33, 0xee, 0x87, 0x00, + 0x57, 0xff, 0x1f, 0xaa, 0xc5, 0x40, 0x23, 0x11, 0x88, 0x80, +}; +const uint8_t _A_GPIO_14_7[] = { + 0x01, 0x00, 0x12, 0x00, 0xa2, 0x41, 0x00, 0x2b, 0xee, 0x87, 0x00, + 0x54, 0x16, 0x7f, 0xf8, 0xfc, 0x46, 0x23, 0x55, 0x8a, 0x80, 0xc4, +}; +const uint8_t* const _A_GPIO_14[] = { + _A_GPIO_14_0, + _A_GPIO_14_1, + _A_GPIO_14_2, + _A_GPIO_14_3, + _A_GPIO_14_4, + _A_GPIO_14_5, + _A_GPIO_14_6, + _A_GPIO_14_7}; + +const uint8_t _A_Infrared_14_0[] = { + 0x01, 0x00, 0x1a, 0x00, 0xfc, 0x41, 0xe0, 0xd1, 0x88, 0x0c, 0x83, 0xe1, 0x03, 0x84, 0x41, + 0x01, 0x63, 0xe0, 0x80, 0x84, 0x4c, 0x0a, 0x20, 0xd1, 0x0a, 0x88, 0x04, 0x7f, 0xf3, 0xf0, +}; +const uint8_t _A_Infrared_14_1[] = { + 0x01, 0x00, 0x17, 0x00, 0xfc, 0x41, 0xe0, 0xd1, 0x88, 0x0c, 0x83, 0xe1, 0x03, 0x84, + 0x41, 0x02, 0x2f, 0xe0, 0x80, 0x83, 0x44, 0x2a, 0x20, 0x11, 0xff, 0xcf, 0xc0, +}; +const uint8_t _A_Infrared_14_2[] = { + 0x01, 0x00, 0x13, 0x00, 0xfc, 0x41, 0xe0, 0xd1, 0x88, 0x0c, 0x80, 0x23, + 0x7e, 0x08, 0x0f, 0xc2, 0x06, 0x15, 0x10, 0x08, 0xff, 0xe7, 0xe0, +}; +const uint8_t _A_Infrared_14_3[] = { + 0x01, + 0x00, + 0x0e, + 0x00, + 0x00, + 0x78, + 0x00, + 0x7c, + 0x10, + 0x1f, + 0x84, + 0x0f, + 0xf1, + 0x07, + 0x00, + 0x8f, + 0xfe, + 0x7e, +}; +const uint8_t _A_Infrared_14_4[] = { + 0x01, + 0x00, + 0x0e, + 0x00, + 0x00, + 0x5f, + 0x82, + 0x02, + 0x05, + 0x5f, + 0x84, + 0x0f, + 0xf1, + 0x07, + 0x00, + 0x8f, + 0xfe, + 0x7e, +}; +const uint8_t _A_Infrared_14_5[] = { + 0x01, 0x00, 0x15, 0x00, 0x00, 0x2f, 0xc2, 0x07, 0x08, 0x82, 0x01, 0x47, 0xc1, + 0x01, 0x05, 0x98, 0x14, 0x41, 0xa3, 0xf8, 0x83, 0x80, 0x47, 0xff, 0x3f, +}; +const uint8_t* const _A_Infrared_14[] = { + _A_Infrared_14_0, + _A_Infrared_14_1, + _A_Infrared_14_2, + _A_Infrared_14_3, + _A_Infrared_14_4, + _A_Infrared_14_5}; + +const uint8_t _A_NFC_14_0[] = { + 0x00, 0x00, 0x08, 0x00, 0x10, 0x00, 0x12, 0x00, 0x22, 0x42, 0x24, 0x87, 0x24, 0x8d, 0x24, + 0x99, 0x24, 0xf1, 0x24, 0x62, 0x24, 0x00, 0x22, 0x00, 0x12, 0x00, 0x10, 0x00, 0x08, +}; +const uint8_t _A_NFC_14_1[] = { + 0x01, 0x00, 0x1a, 0x00, 0x80, 0x42, 0x20, 0x11, 0x00, 0x09, 0x48, 0x28, 0x52, 0x0c, 0x3c, + 0x83, 0x1b, 0x20, 0xcc, 0xc8, 0x3e, 0x32, 0x0b, 0x14, 0x80, 0x1a, 0x21, 0x34, 0x84, 0x00, +}; +const uint8_t _A_NFC_14_2[] = { + 0x01, 0x00, 0x10, 0x00, 0x00, 0x3d, 0x0a, 0x01, 0x87, 0x80, + 0x63, 0x60, 0x19, 0x98, 0x07, 0xc6, 0x01, 0x62, 0x09, 0xc0, +}; +const uint8_t _A_NFC_14_3[] = { + 0x01, 0x00, 0x16, 0x00, 0x00, 0x24, 0x08, 0x02, 0x34, 0x28, 0x26, 0x1e, 0x09, + 0x8d, 0x82, 0x66, 0x60, 0x9f, 0x18, 0x25, 0x8a, 0x08, 0x0f, 0x30, 0xb1, 0x80, +}; +const uint8_t* const _A_NFC_14[] = {_A_NFC_14_0, _A_NFC_14_1, _A_NFC_14_2, _A_NFC_14_3}; + +const uint8_t _A_Plugins_14_0[] = { + 0x00, 0xe7, 0x00, 0xa5, 0x00, 0x99, 0x01, 0x01, 0x02, 0x01, 0x02, 0x81, 0x01, 0x81, 0x0e, + 0xe7, 0x08, 0x24, 0x18, 0x58, 0x20, 0x40, 0x20, 0x30, 0x18, 0x10, 0x08, 0xf0, 0x0f, +}; +const uint8_t _A_Plugins_14_1[] = { + 0x00, 0x70, 0x0e, 0x50, 0x0a, 0x90, 0x19, 0x10, 0x20, 0x10, 0x20, 0x18, 0x18, 0x1e, 0x08, + 0x72, 0x0e, 0x46, 0x02, 0x88, 0x05, 0x08, 0x04, 0x06, 0x03, 0x02, 0x01, 0xfe, 0x01, +}; +const uint8_t _A_Plugins_14_2[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xe4, 0x1c, 0xa7, 0x14, 0x21, 0x33, 0x23, 0x00, + 0x24, 0x00, 0x24, 0x30, 0x23, 0x10, 0xe1, 0x1c, 0xff, 0x04, 0x00, 0x03, 0x00, 0x00, +}; +const uint8_t _A_Plugins_14_3[] = { + 0x00, 0x30, 0x00, 0x48, 0x00, 0xce, 0x01, 0x02, 0x01, 0x3e, 0x07, 0x28, 0x05, 0xc8, 0x0c, + 0x0e, 0x10, 0x0a, 0x10, 0x0e, 0x0c, 0x08, 0x04, 0x38, 0x07, 0x20, 0x01, 0xc0, 0x00, +}; +const uint8_t _A_Plugins_14_4[] = { + 0x00, 0x40, 0x02, 0x70, 0x0e, 0x10, 0x08, 0x30, 0x18, 0xce, 0x21, 0x4a, 0x21, 0x32, 0x1b, + 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x03, 0x02, 0x01, 0xce, 0x01, 0x48, 0x00, 0x30, 0x00, +}; +const uint8_t _A_Plugins_14_5[] = { + 0x00, 0x00, 0x0c, 0x00, 0x12, 0x80, 0x33, 0x80, 0x00, 0xb9, 0x01, 0x29, 0x02, 0x66, 0x02, + 0x80, 0x01, 0x80, 0x00, 0x60, 0x3f, 0x20, 0x00, 0x39, 0x00, 0x09, 0x00, 0x06, 0x00, +}; +const uint8_t _A_Plugins_14_6[] = { + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0xf3, 0x39, 0x52, 0x20, 0xcc, 0x20, 0x00, 0x01, + 0x00, 0x01, 0xc0, 0x20, 0x40, 0x20, 0xf3, 0x3f, 0x12, 0x00, 0x0c, 0x00, 0x00, 0x00, +}; +const uint8_t _A_Plugins_14_7[] = { + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x12, 0xb9, 0x33, 0xa9, 0x00, 0x66, 0x01, 0x80, 0x02, + 0x80, 0x02, 0x60, 0x01, 0xa0, 0x00, 0xb9, 0x3f, 0x09, 0x00, 0x06, 0x00, 0x00, 0x00, +}; +const uint8_t _A_Plugins_14_8[] = { + 0x00, 0x00, 0x00, 0x39, 0x00, 0x29, 0x00, 0x66, 0x06, 0x80, 0x09, 0x80, 0x39, 0x60, 0x20, + 0xe0, 0x20, 0x39, 0x01, 0x09, 0x01, 0xc6, 0x20, 0x40, 0x20, 0xc0, 0x3f, 0x00, 0x00, +}; +const uint8_t* const _A_Plugins_14[] = { + _A_Plugins_14_0, + _A_Plugins_14_1, + _A_Plugins_14_2, + _A_Plugins_14_3, + _A_Plugins_14_4, + _A_Plugins_14_5, + _A_Plugins_14_6, + _A_Plugins_14_7, + _A_Plugins_14_8}; + +const uint8_t _A_Settings_14_0[] = { + 0x00, 0x03, 0x07, 0x87, 0x04, 0x8e, 0x02, 0x9c, 0x32, 0xf8, 0x2c, 0x50, 0x20, 0x30, 0x1e, + 0x1e, 0x03, 0x81, 0x04, 0xcd, 0x09, 0x53, 0x13, 0x50, 0x26, 0x48, 0x2c, 0x38, 0x18, +}; +const uint8_t _A_Settings_14_1[] = { + 0x00, 0x03, 0x00, 0x87, 0x03, 0x4e, 0x02, 0x7c, 0x01, 0x48, 0x19, 0x58, 0x16, 0x30, 0x10, + 0x10, 0x0f, 0x8f, 0x04, 0xc0, 0x09, 0x26, 0x13, 0x29, 0x16, 0x28, 0x0c, 0x24, 0x00, +}; +const uint8_t _A_Settings_14_2[] = { + 0x00, 0x03, 0x00, 0x07, 0x00, 0xde, 0x01, 0x24, 0x01, 0xac, 0x00, 0xb8, 0x0c, 0x30, 0x0b, + 0x10, 0x08, 0x88, 0x07, 0xc7, 0x09, 0x20, 0x0b, 0x13, 0x06, 0x14, 0x00, 0x14, 0x00, +}; +const uint8_t _A_Settings_14_3[] = { + 0x00, 0x04, 0x0c, 0x09, 0x00, 0x13, 0x20, 0x26, 0x20, 0x4c, 0x00, 0xb8, 0x00, 0xa4, 0x00, + 0x74, 0x00, 0x94, 0x01, 0x64, 0x01, 0x02, 0x01, 0xf1, 0x18, 0x08, 0x38, 0x04, 0x30, +}; +const uint8_t _A_Settings_14_4[] = { + 0x00, 0x04, 0x0f, 0x89, 0x00, 0x93, 0x26, 0xa6, 0x29, 0x2c, 0x28, 0x18, 0x24, 0x00, 0x1c, + 0x0e, 0x00, 0x09, 0x00, 0x05, 0x06, 0x65, 0x0e, 0x59, 0x1c, 0x40, 0x38, 0x3c, 0x30, +}; +const uint8_t _A_Settings_14_5[] = { + 0x00, 0x04, 0x08, 0x05, 0x04, 0xc3, 0x23, 0x20, 0x10, 0xa0, 0x09, 0x60, 0x0a, 0x00, 0x0a, + 0x80, 0x09, 0x80, 0x07, 0x03, 0x07, 0x02, 0x0e, 0x01, 0x3c, 0x19, 0x08, 0x16, 0x18, +}; +const uint8_t _A_Settings_14_6[] = { + 0x00, 0x00, 0x14, 0x00, 0x24, 0x00, 0x02, 0x18, 0x31, 0xf8, 0x08, 0x08, 0x04, 0x68, 0x02, + 0xd8, 0x02, 0x80, 0x06, 0xc0, 0x0a, 0xc0, 0x13, 0x00, 0x26, 0x00, 0x0c, 0x00, 0x18, +}; +const uint8_t _A_Settings_14_7[] = { + 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x0c, 0x32, 0x1c, 0x01, 0xb8, 0x38, 0x78, 0x04, 0x04, 0x02, + 0x34, 0x03, 0x4c, 0x05, 0x40, 0x09, 0x20, 0x13, 0xe0, 0x26, 0x00, 0x0c, 0x00, 0x18, +}; +const uint8_t _A_Settings_14_8[] = { + 0x00, 0x00, 0x09, 0x06, 0x05, 0x0e, 0x25, 0x1c, 0x19, 0xb8, 0x00, 0x70, 0x3c, 0x3c, 0x02, + 0x02, 0x03, 0x9a, 0x04, 0xa6, 0x09, 0xa0, 0x13, 0x90, 0x26, 0x70, 0x0c, 0x00, 0x18, +}; +const uint8_t _A_Settings_14_9[] = { + 0x00, 0x03, 0x07, 0x87, 0x04, 0x8e, 0x02, 0x9c, 0x32, 0xf8, 0x2c, 0x50, 0x20, 0x30, 0x1e, + 0x1e, 0x03, 0x81, 0x04, 0xcd, 0x09, 0x53, 0x13, 0x50, 0x26, 0x48, 0x2c, 0x38, 0x18, +}; +const uint8_t* const _A_Settings_14[] = { + _A_Settings_14_0, + _A_Settings_14_1, + _A_Settings_14_2, + _A_Settings_14_3, + _A_Settings_14_4, + _A_Settings_14_5, + _A_Settings_14_6, + _A_Settings_14_7, + _A_Settings_14_8, + _A_Settings_14_9}; + +const uint8_t _A_Sub1ghz_14_0[] = { + 0x01, 0x00, 0x1a, 0x00, 0x82, 0x42, 0x20, 0x51, 0x08, 0x4c, 0x92, 0x0b, 0x28, 0xea, 0xca, + 0x80, 0x22, 0x05, 0x1e, 0x4c, 0x93, 0x85, 0x10, 0xe2, 0x42, 0x38, 0x10, 0x00, 0x0a, 0x80, +}; +const uint8_t _A_Sub1ghz_14_1[] = { + 0x01, 0x00, 0x18, 0x00, 0x82, 0x42, 0x20, 0x51, 0x08, 0x4c, 0x92, 0x0b, 0x28, 0xe2, + 0x80, 0x48, 0x0a, 0x3c, 0x99, 0x27, 0x0a, 0x21, 0xc4, 0x84, 0x70, 0x20, 0x00, 0x15, +}; +const uint8_t _A_Sub1ghz_14_2[] = { + 0x01, 0x00, 0x16, 0x00, 0x82, 0x42, 0x20, 0x51, 0x08, 0x0c, 0x80, 0x02, 0x3c, + 0x10, 0x09, 0x01, 0x4f, 0x85, 0x10, 0xe2, 0x42, 0x38, 0x10, 0x00, 0x0a, 0x80, +}; +const uint8_t _A_Sub1ghz_14_3[] = { + 0x01, + 0x00, + 0x08, + 0x00, + 0x00, + 0x3f, + 0x00, + 0x02, + 0x40, + 0x55, + 0x00, + 0xc8, +}; +const uint8_t _A_Sub1ghz_14_4[] = { + 0x01, + 0x00, + 0x0a, + 0x00, + 0x00, + 0x3f, + 0x42, + 0x04, + 0x01, + 0x10, + 0x28, + 0xf0, + 0x00, + 0x38, +}; +const uint8_t _A_Sub1ghz_14_5[] = { + 0x01, 0x00, 0x12, 0x00, 0x00, 0x1c, 0x22, 0x09, 0x04, 0x84, 0x75, + 0x21, 0x40, 0x11, 0x02, 0x8f, 0x22, 0x09, 0xc0, 0x80, 0x00, 0x64, +}; +const uint8_t* const _A_Sub1ghz_14[] = { + _A_Sub1ghz_14_0, + _A_Sub1ghz_14_1, + _A_Sub1ghz_14_2, + _A_Sub1ghz_14_3, + _A_Sub1ghz_14_4, + _A_Sub1ghz_14_5}; + +const uint8_t _A_U2F_14_0[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x10, 0x02, 0x08, 0x04, 0xfe, 0x1f, 0x01, 0x20, + 0xd5, 0x2d, 0x55, 0x25, 0x15, 0x2d, 0x95, 0x24, 0xdd, 0x25, 0x01, 0x20, 0xfe, 0x1f, +}; +const uint8_t _A_U2F_14_1[] = { + 0x00, 0x00, 0x00, 0xe0, 0x01, 0x10, 0x02, 0x08, 0x04, 0x08, 0x04, 0xfe, 0x1f, 0x01, 0x20, + 0xd5, 0x2d, 0x55, 0x25, 0x15, 0x2d, 0x95, 0x24, 0xdd, 0x25, 0x01, 0x20, 0xfe, 0x1f, +}; +const uint8_t _A_U2F_14_2[] = { + 0x00, 0xe0, 0x01, 0x10, 0x02, 0x08, 0x04, 0x08, 0x04, 0x08, 0x00, 0xfe, 0x1f, 0x01, 0x20, + 0xd5, 0x2d, 0x55, 0x25, 0x15, 0x2d, 0x95, 0x24, 0xdd, 0x25, 0x01, 0x20, 0xfe, 0x1f, +}; +const uint8_t _A_U2F_14_3[] = { + 0x00, 0x00, 0x00, 0xe0, 0x01, 0x10, 0x02, 0x08, 0x04, 0x08, 0x04, 0xfe, 0x1f, 0x01, 0x20, + 0xd5, 0x2d, 0x55, 0x25, 0x15, 0x2d, 0x95, 0x24, 0xdd, 0x25, 0x01, 0x20, 0xfe, 0x1f, +}; +const uint8_t* const _A_U2F_14[] = {_A_U2F_14_0, _A_U2F_14_1, _A_U2F_14_2, _A_U2F_14_3}; + +const uint8_t _A_UniRFRemix_14_0[] = { + 0x01, 0x00, 0x14, 0x00, 0xfc, 0x41, 0xe0, 0x90, 0x8e, 0x14, 0x43, 0xc5, + 0x11, 0x00, 0x88, 0x14, 0x60, 0x40, 0x12, 0x05, 0xb8, 0x54, 0x43, 0x22, +}; +const uint8_t _A_UniRFRemix_14_1[] = { + 0x01, 0x00, 0x12, 0x00, 0xf8, 0x40, 0x61, 0x10, 0x2a, 0x24, 0x13, + 0xc8, 0x01, 0x20, 0x28, 0x80, 0x0c, 0x16, 0xe1, 0x51, 0x0c, 0x88, +}; +const uint8_t _A_UniRFRemix_14_2[] = { + 0x01, + 0x00, + 0x05, + 0x00, + 0xe0, + 0x00, + 0x7c, + 0x03, + 0x40, +}; +const uint8_t _A_UniRFRemix_14_3[] = { + 0x01, + 0x00, + 0x0d, + 0x00, + 0xf8, + 0x40, + 0x61, + 0x10, + 0x28, + 0x20, + 0x03, + 0xc0, + 0x04, + 0x15, + 0x10, + 0xc8, + 0x80, +}; +const uint8_t _A_UniRFRemix_14_4[] = { + 0x01, + 0x00, + 0x0e, + 0x00, + 0xfc, + 0x41, + 0xe0, + 0x90, + 0x88, + 0x14, + 0x40, + 0x03, + 0xe0, + 0x11, + 0x0a, + 0x88, + 0x64, + 0x40, +}; +const uint8_t _A_UniRFRemix_14_5[] = { + 0x01, + 0x00, + 0x0d, + 0x00, + 0xf8, + 0x40, + 0x61, + 0x10, + 0x28, + 0x20, + 0x03, + 0xc0, + 0x04, + 0x15, + 0x10, + 0xc8, + 0x80, +}; +const uint8_t _A_UniRFRemix_14_6[] = { + 0x01, + 0x00, + 0x05, + 0x00, + 0xe0, + 0x00, + 0x7c, + 0x03, + 0x40, +}; +const uint8_t _A_UniRFRemix_14_7[] = { + 0x01, 0x00, 0x12, 0x00, 0xf8, 0x40, 0x61, 0x10, 0x2a, 0x24, 0x13, + 0xc8, 0x01, 0x20, 0x28, 0x80, 0x0c, 0x16, 0xe1, 0x51, 0x0c, 0x88, +}; +const uint8_t* const _A_UniRFRemix_14[] = { + _A_UniRFRemix_14_0, + _A_UniRFRemix_14_1, + _A_UniRFRemix_14_2, + _A_UniRFRemix_14_3, + _A_UniRFRemix_14_4, + _A_UniRFRemix_14_5, + _A_UniRFRemix_14_6, + _A_UniRFRemix_14_7}; + +const uint8_t _A_iButton_14_0[] = { + 0x00, 0x00, 0x1c, 0x00, 0x3e, 0x00, 0x35, 0x80, 0x3a, 0x78, 0x15, 0x84, 0x0a, 0x32, 0x05, + 0x49, 0x02, 0x85, 0x02, 0x85, 0x02, 0x49, 0x02, 0x32, 0x01, 0x84, 0x00, 0x78, 0x00, +}; +const uint8_t _A_iButton_14_1[] = { + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x26, 0x80, 0x21, 0xe0, 0x10, 0x38, 0x0d, 0x6c, 0x03, + 0x56, 0x01, 0x2b, 0x01, 0x97, 0x00, 0x4d, 0x00, 0x21, 0x00, 0x1e, 0x00, 0x00, 0x00, +}; +const uint8_t _A_iButton_14_2[] = { + 0x01, 0x00, 0x1a, 0x00, 0x00, 0x24, 0xc2, 0x01, 0x2c, 0x80, 0x48, 0xfb, 0x11, 0x89, 0x64, + 0x1b, 0x2d, 0x01, 0xa5, 0xc0, 0x24, 0xb0, 0x08, 0x94, 0x02, 0x13, 0x00, 0x83, 0x85, 0x88, +}; +const uint8_t _A_iButton_14_3[] = { + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x26, 0x80, 0x21, 0x60, 0x18, 0x98, 0x06, 0x04, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x81, 0x00, 0x41, 0x00, 0x21, 0x00, 0x1e, 0x00, 0x00, 0x00, +}; +const uint8_t _A_iButton_14_4[] = { + 0x01, 0x00, 0x1a, 0x00, 0x80, 0x47, 0x20, 0x13, 0xe8, 0x04, 0xd7, 0x01, 0x3a, 0xbc, 0x45, + 0x70, 0x90, 0xa8, 0x14, 0x16, 0x03, 0x02, 0x00, 0xa8, 0x08, 0x70, 0x90, 0x0b, 0xc4, 0x00, +}; +const uint8_t _A_iButton_14_5[] = { + 0x01, 0x00, 0x1a, 0x00, 0x00, 0x14, 0xe2, 0x01, 0x24, 0x80, 0x48, 0xb0, 0x11, 0x1f, 0x04, + 0x22, 0x31, 0x05, 0x83, 0x40, 0xa0, 0x20, 0x13, 0x80, 0xf0, 0x60, 0x13, 0xe0, 0xc1, 0x00, +}; +const uint8_t _A_iButton_14_6[] = { + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x24, 0x00, 0x23, 0x80, 0x20, 0xf0, 0x10, 0x0c, 0x0d, + 0xe2, 0x02, 0x91, 0x01, 0x69, 0x01, 0x15, 0x01, 0x8d, 0x00, 0x4d, 0x00, 0x3e, 0x00, +}; +const uint8_t* const _A_iButton_14[] = { + _A_iButton_14_0, + _A_iButton_14_1, + _A_iButton_14_2, + _A_iButton_14_3, + _A_iButton_14_4, + _A_iButton_14_5, + _A_iButton_14_6}; + +const uint8_t _I_ArrowC_1_36x36_0[] = { + 0x01, 0x00, 0x7a, 0x00, 0x00, 0x78, 0x01, 0x30, 0x13, 0xf0, 0x21, 0x5c, 0x38, 0x00, 0x42, 0x86, + 0x40, 0x20, 0x20, 0x41, 0x81, 0x4f, 0xc0, 0x85, 0xe1, 0x07, 0x16, 0x02, 0x20, 0x1c, 0x38, 0x44, + 0x02, 0x41, 0x00, 0xa0, 0x41, 0x20, 0x15, 0x00, 0x21, 0xe2, 0x01, 0x63, 0x02, 0x80, 0x41, 0x20, + 0x20, 0x42, 0x81, 0x40, 0xa2, 0x02, 0x51, 0x80, 0xc1, 0x00, 0x83, 0x80, 0x80, 0x42, 0x00, 0x85, + 0x8f, 0x87, 0xc7, 0xe3, 0xe1, 0xd2, 0x80, 0x8c, 0x44, 0x08, 0x30, 0x21, 0x88, 0x81, 0x06, 0x08, + 0x2c, 0x1a, 0x20, 0x64, 0x44, 0xe2, 0x04, 0x11, 0x39, 0x48, 0xa0, 0x1a, 0x02, 0x40, 0x81, 0x06, + 0x80, 0x04, 0x18, 0x89, 0x44, 0xca, 0xc5, 0x30, 0xb8, 0x09, 0x06, 0x01, 0xc8, 0x87, 0x89, 0x04, + 0x46, 0x30, 0x08, 0x18, 0x38, 0xbc, 0x01, 0x72, 0xe0, 0x10, 0x44, 0x01, 0xe0, 0x04, +}; +const uint8_t* const _I_ArrowC_1_36x36[] = {_I_ArrowC_1_36x36_0}; + +const uint8_t _I_Detailed_chip_17x13_0[] = { + 0x01, 0x00, 0x1e, 0x00, 0xfe, 0x5f, 0xe0, 0x10, 0x2c, 0x04, 0x02, 0x23, + 0x11, 0x80, 0xe4, 0x62, 0x50, 0x1a, 0xff, 0xc2, 0x03, 0x21, 0x84, 0x00, + 0x9a, 0xbf, 0xf4, 0x08, 0x98, 0x5c, 0x83, 0xa4, 0x23, 0x20, +}; +const uint8_t* const _I_Detailed_chip_17x13[] = {_I_Detailed_chip_17x13_0}; + +const uint8_t _I_Keychain_39x36_0[] = { + 0x01, 0x00, 0x82, 0x00, 0x00, 0x0c, 0x3c, 0x06, 0x5c, 0x09, 0x80, 0x10, 0xa4, 0x14, 0x00, + 0x21, 0x44, 0x30, 0x00, 0x42, 0x84, 0x40, 0x20, 0x20, 0x46, 0x82, 0x40, 0x20, 0x40, 0x4b, + 0x0c, 0x4d, 0xf8, 0x4e, 0x7e, 0x7f, 0x84, 0x0e, 0x01, 0x80, 0xe0, 0xff, 0xc8, 0x22, 0x04, + 0x18, 0xfc, 0x70, 0x08, 0x36, 0x09, 0xec, 0x60, 0x08, 0x33, 0x0b, 0x68, 0x10, 0xb6, 0x20, + 0x47, 0xe0, 0x7d, 0xed, 0x80, 0x41, 0xf0, 0x5f, 0xed, 0x81, 0xd1, 0xf8, 0x58, 0x7f, 0x13, + 0xf8, 0x07, 0xe3, 0x81, 0xff, 0xbf, 0xc0, 0x2f, 0x98, 0x08, 0x7f, 0xe2, 0x19, 0xfe, 0x80, + 0x41, 0xfe, 0x10, 0xfa, 0x78, 0x10, 0x7c, 0x10, 0x78, 0xf8, 0x10, 0x78, 0x16, 0x38, 0xd8, + 0x10, 0x70, 0x17, 0x72, 0x80, 0xc1, 0xe0, 0x02, 0x31, 0x02, 0x14, 0x78, 0x08, 0x21, 0x51, + 0x86, 0x10, 0x45, 0x34, 0x78, 0x00, 0x70, 0x81, 0x08, 0x1c, 0x20, 0x46, 0x57, 0x18, +}; +const uint8_t* const _I_Keychain_39x36[] = {_I_Keychain_39x36_0}; + +const uint8_t _I_Medium_chip_22x21_0[] = { + 0x01, 0x00, 0x35, 0x00, 0xfe, 0x7f, 0xe1, 0xf0, 0x28, 0x04, 0x43, 0xf3, 0xff, 0x93, 0xe1, + 0x6a, 0x52, 0x8e, 0x2f, 0xfe, 0x51, 0x25, 0x80, 0x4a, 0x72, 0xb6, 0x79, 0x55, 0x76, 0xc1, + 0x2e, 0xaa, 0xc0, 0x25, 0x51, 0xdc, 0x00, 0x14, 0x70, 0x00, 0x56, 0xae, 0x81, 0x47, 0x2b, + 0x7d, 0x95, 0x07, 0x48, 0x46, 0x42, 0x92, 0x17, 0x90, 0xd4, 0x87, 0x64, +}; +const uint8_t* const _I_Medium_chip_22x21[] = {_I_Medium_chip_22x21_0}; + +const uint8_t _I_Modern_reader_18x34_0[] = { + 0x01, 0x00, 0x41, 0x00, 0xff, 0x5f, 0xe0, 0x10, 0x7c, 0x04, 0x02, 0x97, 0x20, 0x80, + 0xc4, 0xe0, 0x10, 0x2e, 0x9f, 0xfe, 0x05, 0xe3, 0xff, 0xc0, 0xfe, 0x7c, 0x00, 0x10, + 0x81, 0x48, 0x05, 0x00, 0xe1, 0xb9, 0xef, 0xe0, 0x7b, 0x3b, 0x08, 0x20, 0x7b, 0xc0, + 0xe4, 0xf9, 0x80, 0x90, 0x10, 0x40, 0xe1, 0x0a, 0x10, 0x68, 0x85, 0xfc, 0x41, 0x22, + 0x39, 0x13, 0x48, 0xa6, 0x45, 0x92, 0xff, 0x97, 0xc7, 0xfd, 0x3f, 0x80, 0x00, +}; +const uint8_t* const _I_Modern_reader_18x34[] = {_I_Modern_reader_18x34_0}; + +const uint8_t _I_Move_flipper_26x39_0[] = { + 0x01, 0x00, 0x67, 0x00, 0x80, 0x78, 0x20, 0xe0, 0x31, 0x88, 0x42, 0x40, 0x63, 0x88, 0x88, 0x00, + 0xc6, 0x89, 0x20, 0x01, 0x8c, 0x8a, 0x80, 0x03, 0x18, 0x96, 0x00, 0x06, 0x21, 0x21, 0x80, 0x80, + 0xd6, 0x7f, 0x02, 0x80, 0x62, 0x68, 0x00, 0x3f, 0x01, 0xa4, 0x40, 0x06, 0x51, 0x80, 0x19, 0x7f, + 0x00, 0x65, 0xfe, 0x01, 0x97, 0xfc, 0x06, 0xdf, 0xe1, 0x99, 0x7f, 0x07, 0x62, 0x1b, 0x10, 0x19, + 0x08, 0xc6, 0x38, 0x2b, 0x18, 0x4c, 0x88, 0x06, 0x54, 0x10, 0x19, 0x52, 0x40, 0x63, 0x0d, 0x01, + 0x98, 0x2c, 0xa1, 0xa1, 0x32, 0x86, 0x86, 0xcc, 0xc6, 0x70, 0x10, 0x18, 0xe2, 0x70, 0x47, 0x32, + 0xa0, 0x80, 0xc9, 0x06, 0x51, 0x24, 0x59, 0x7f, 0x21, 0xf0, 0x00, +}; +const uint8_t* const _I_Move_flipper_26x39[] = {_I_Move_flipper_26x39_0}; + +const uint8_t _I_NFC_dolphin_emulation_47x61_0[] = { + 0x01, 0x00, 0x20, 0x01, 0x00, 0x0f, 0xfa, 0x3e, 0x04, 0x2e, 0x04, 0x07, 0xc1, 0x01, 0x02, 0x8c, + 0xc0, 0x04, 0x30, 0x60, 0x10, 0xc3, 0x00, 0x43, 0x18, 0x01, 0x0c, 0x08, 0x04, 0x32, 0x00, 0x10, + 0x85, 0x0a, 0x81, 0x00, 0xc0, 0x08, 0x08, 0x06, 0x20, 0x71, 0x00, 0x14, 0x04, 0x26, 0x41, 0xb2, + 0x02, 0x9c, 0xf2, 0x09, 0x10, 0x02, 0x95, 0x08, 0x0a, 0x98, 0x18, 0x44, 0x20, 0x70, 0x38, 0x08, + 0xc4, 0x26, 0x10, 0x01, 0x1c, 0x04, 0x92, 0x13, 0x84, 0x88, 0x01, 0x4a, 0x23, 0xc4, 0x88, 0x44, + 0x00, 0xa5, 0x44, 0x02, 0xa5, 0x1a, 0x41, 0x20, 0x02, 0x94, 0xa8, 0x0a, 0x93, 0x69, 0x05, 0x00, + 0x52, 0x31, 0x18, 0xdf, 0x80, 0xa2, 0x38, 0x18, 0x84, 0x37, 0xc0, 0x51, 0x1a, 0x14, 0x42, 0x0e, + 0x7b, 0x29, 0xe4, 0x62, 0x07, 0xf0, 0x36, 0x14, 0x52, 0x03, 0xf0, 0x81, 0x81, 0x46, 0x15, 0x00, + 0xe8, 0x7f, 0xc1, 0x83, 0x08, 0x02, 0xa2, 0x34, 0x63, 0x1f, 0xf0, 0x88, 0xc3, 0x81, 0xa3, 0x3c, + 0x80, 0x47, 0xc0, 0xa2, 0x98, 0x1b, 0x06, 0x00, 0xd2, 0x21, 0x31, 0x0d, 0x8c, 0x14, 0x56, 0x2a, + 0x32, 0x83, 0xab, 0x4d, 0x68, 0x4b, 0x62, 0x80, 0x41, 0x06, 0xe4, 0x05, 0x30, 0xc8, 0x80, 0x04, + 0x3e, 0x30, 0xb0, 0x29, 0xe0, 0x20, 0xac, 0x92, 0x88, 0x70, 0x15, 0xe7, 0x10, 0xa0, 0x41, 0x78, + 0x0d, 0x22, 0xd0, 0x0c, 0xc4, 0xa6, 0x32, 0x0a, 0x80, 0x0c, 0x46, 0x23, 0x40, 0xd8, 0x10, 0x84, + 0x4e, 0x34, 0x0a, 0xc4, 0x02, 0xd4, 0xf7, 0x1a, 0x06, 0xb2, 0x01, 0xd4, 0xa0, 0x81, 0x46, 0xa9, + 0x00, 0xd4, 0x60, 0xe0, 0x34, 0x09, 0x54, 0x02, 0xa1, 0x0f, 0x81, 0x50, 0x22, 0xb0, 0x0d, 0x04, + 0x7b, 0xf9, 0x5f, 0x23, 0x0d, 0x02, 0xfb, 0x00, 0x98, 0x0b, 0x87, 0x41, 0xfa, 0x80, 0xba, 0x0f, + 0xeb, 0x07, 0x18, 0x88, 0x00, 0x7d, 0x5f, 0xf3, 0xb8, 0x80, 0x07, 0xa1, 0x5f, 0x02, 0x10, 0x60, + 0x73, 0xc0, 0x53, 0xa0, 0x65, 0x40, 0xa7, 0x00, 0xcd, 0x01, 0x52, 0x20, 0xc8, 0x23, 0xe1, 0x01, + 0x6c, 0xea, 0x20, 0x54, +}; +const uint8_t* const _I_NFC_dolphin_emulation_47x61[] = {_I_NFC_dolphin_emulation_47x61_0}; + +const uint8_t _I_NFC_manual_60x50_0[] = { + 0x01, 0x00, 0xd1, 0x00, 0x80, 0x7c, 0x3f, 0xe0, 0x02, 0x83, 0x81, 0xc6, 0x18, 0x0c, 0x38, 0xc0, + 0x08, 0x62, 0x40, 0xd1, 0x8f, 0xfc, 0x24, 0x00, 0x71, 0xc4, 0x80, 0x86, 0x41, 0x0c, 0xa1, 0xc0, + 0x30, 0x18, 0x00, 0x39, 0x40, 0xb0, 0x90, 0x0a, 0x06, 0x08, 0x0e, 0x56, 0x18, 0x44, 0x06, 0x41, + 0x81, 0x03, 0x96, 0x46, 0x11, 0x02, 0x88, 0x05, 0xcc, 0x0e, 0x30, 0x4a, 0x40, 0x1c, 0xec, 0x3c, + 0x80, 0x40, 0x89, 0xd2, 0x49, 0x09, 0xa1, 0x01, 0xce, 0x19, 0x23, 0x84, 0xe0, 0x40, 0xe7, 0xf0, + 0xe0, 0x0c, 0x06, 0x02, 0x4f, 0x1c, 0xf8, 0x28, 0x61, 0x10, 0x38, 0x11, 0xb5, 0x21, 0x10, 0x58, + 0x20, 0x1d, 0x21, 0x50, 0x9e, 0x87, 0x02, 0x73, 0x08, 0xde, 0x63, 0x61, 0x31, 0x14, 0x2a, 0xc2, + 0x24, 0x20, 0x76, 0xa1, 0x01, 0xdb, 0x09, 0x01, 0x03, 0xa4, 0x32, 0x11, 0xfe, 0x4f, 0x9f, 0xfe, + 0x17, 0x10, 0x0b, 0xac, 0x31, 0x32, 0x4b, 0x32, 0x83, 0xc3, 0xa8, 0x00, 0xf5, 0x87, 0x60, 0x04, + 0xea, 0x55, 0x11, 0x3a, 0xc0, 0xc1, 0x65, 0xe0, 0x02, 0x18, 0x1e, 0x3f, 0x8d, 0xf6, 0x3f, 0x88, + 0x3c, 0xa1, 0x00, 0x7f, 0x03, 0xf8, 0x1c, 0xff, 0x91, 0xff, 0xe0, 0x1d, 0x3f, 0x10, 0xf9, 0x94, + 0x24, 0x0e, 0x66, 0xd1, 0xa5, 0x01, 0xd0, 0x3e, 0x20, 0x75, 0xe0, 0x40, 0x42, 0xea, 0xe1, 0x11, + 0x3a, 0xab, 0x8c, 0x0f, 0xe0, 0x21, 0xf9, 0x5f, 0xff, 0xd7, 0xf0, 0x7e, 0x7e, 0x44, 0xfe, 0x07, + 0x55, 0xba, 0x43, 0xd4, 0xf0, +}; +const uint8_t* const _I_NFC_manual_60x50[] = {_I_NFC_manual_60x50_0}; + +const uint8_t _I_Release_arrow_18x15_0[] = { + 0x01, 0x00, 0x25, 0x00, 0x80, 0x41, 0x80, 0x43, 0x0a, 0x01, 0x0c, 0x48, 0x04, 0x32, + 0x28, 0x07, 0xfa, 0x97, 0x00, 0x80, 0xe6, 0x20, 0x1f, 0xd9, 0xfc, 0x07, 0xfb, 0x7f, + 0x81, 0x01, 0x48, 0x16, 0x41, 0x12, 0x0b, 0x90, 0x74, 0xc4, 0x64, 0x29, 0x10, +}; +const uint8_t* const _I_Release_arrow_18x15[] = {_I_Release_arrow_18x15_0}; + +const uint8_t _I_Restoring_38x32_0[] = { + 0x01, 0x00, 0x82, 0x00, 0x00, 0x5d, 0xfc, 0x06, 0x5e, 0x0c, 0x04, 0x04, 0x08, 0x31, 0x88, + 0x04, 0x08, 0x08, 0x30, 0x4b, 0xf0, 0x10, 0xb8, 0x5f, 0xf8, 0x18, 0x10, 0x7c, 0x38, 0x10, + 0x99, 0x4c, 0x60, 0x14, 0x08, 0x06, 0x02, 0x11, 0x00, 0xd0, 0x01, 0x0e, 0x21, 0x01, 0xa0, + 0x41, 0x20, 0x10, 0x88, 0x10, 0x10, 0xa0, 0x90, 0x49, 0x00, 0x94, 0x60, 0x50, 0x80, 0x21, + 0x40, 0x62, 0x00, 0x42, 0x8f, 0xc7, 0xe7, 0xd3, 0xe0, 0x21, 0x40, 0xa4, 0x02, 0x22, 0x83, + 0x4c, 0x00, 0x85, 0x0e, 0x9c, 0x40, 0x31, 0x10, 0x08, 0xe4, 0x70, 0x08, 0x53, 0x88, 0x74, + 0x02, 0x89, 0x00, 0xb8, 0x41, 0xc0, 0x85, 0xe0, 0x29, 0x0c, 0x8e, 0x01, 0xc0, 0x29, 0x0c, + 0x84, 0xa8, 0x36, 0x0f, 0x04, 0x40, 0x08, 0x39, 0x08, 0xf9, 0x44, 0x45, 0x43, 0x00, 0x0f, + 0x88, 0x78, 0x78, 0x01, 0xf1, 0x0f, 0x0f, 0xfc, 0xda, 0x58, 0x09, 0xfa, 0x4d, 0x80, +}; +const uint8_t* const _I_Restoring_38x32[] = {_I_Restoring_38x32_0}; + +const uint8_t _I_Tap_reader_36x38_0[] = { + 0x01, 0x00, 0x90, 0x00, 0xfe, 0x4f, 0xc0, 0x85, 0x06, 0xa0, 0x60, 0x00, 0x83, 0x27, 0xa8, + 0x70, 0x00, 0x83, 0x0b, 0xc0, 0x78, 0x21, 0xe0, 0x42, 0x04, 0x94, 0x4e, 0x01, 0x81, 0x02, + 0x0e, 0x4e, 0xff, 0x01, 0x02, 0x0f, 0x8f, 0xff, 0x01, 0xb8, 0x01, 0x07, 0x81, 0x01, 0xe4, + 0x40, 0x3e, 0x7f, 0xf8, 0x1c, 0x92, 0x02, 0x04, 0x28, 0x94, 0x08, 0x08, 0x70, 0x40, 0x21, + 0xc2, 0x3e, 0x7c, 0x08, 0x1f, 0x4a, 0x17, 0x73, 0xdf, 0x81, 0x0b, 0x67, 0x61, 0x02, 0x17, + 0xcf, 0xbc, 0x04, 0x30, 0xc0, 0x81, 0x0b, 0xe6, 0x04, 0x81, 0x3a, 0x06, 0x04, 0x08, 0x22, + 0x22, 0xc9, 0x42, 0x45, 0x85, 0x52, 0x85, 0x0d, 0x8a, 0xb4, 0x04, 0x10, 0xc0, 0xe6, 0xa1, + 0x20, 0x42, 0x8d, 0x07, 0x44, 0x08, 0x35, 0x28, 0x21, 0xa0, 0x8b, 0x22, 0x04, 0x2f, 0xe4, + 0x04, 0xee, 0x88, 0x11, 0x02, 0x12, 0x2c, 0x60, 0x10, 0x20, 0x21, 0xc1, 0x93, 0xe4, 0x9a, + 0x28, 0x3d, 0x00, 0x12, 0x5f, 0xe5, 0x98, 0x84, 0xc7, 0xf9, 0x1f, 0x5c, 0x10, +}; +const uint8_t* const _I_Tap_reader_36x38[] = {_I_Tap_reader_36x38_0}; + +const uint8_t _I_Pin_arrow_down_7x9_0[] = { + 0x00, + 0x1c, + 0x1c, + 0x1c, + 0x1c, + 0x1c, + 0x7f, + 0x3e, + 0x1c, + 0x08, +}; +const uint8_t* const _I_Pin_arrow_down_7x9[] = {_I_Pin_arrow_down_7x9_0}; + +const uint8_t _I_Pin_arrow_left_9x7_0[] = { + 0x00, + 0x08, + 0x00, + 0x0c, + 0x00, + 0xfe, + 0x01, + 0xff, + 0x01, + 0xfe, + 0x01, + 0x0c, + 0x00, + 0x08, + 0x00, +}; +const uint8_t* const _I_Pin_arrow_left_9x7[] = {_I_Pin_arrow_left_9x7_0}; + +const uint8_t _I_Pin_arrow_right_9x7_0[] = { + 0x00, + 0x20, + 0x00, + 0x60, + 0x00, + 0xff, + 0x00, + 0xff, + 0x01, + 0xff, + 0x00, + 0x60, + 0x00, + 0x20, + 0x00, +}; +const uint8_t* const _I_Pin_arrow_right_9x7[] = {_I_Pin_arrow_right_9x7_0}; + +const uint8_t _I_Pin_arrow_up_7x9_0[] = { + 0x00, + 0x08, + 0x1c, + 0x3e, + 0x7f, + 0x1c, + 0x1c, + 0x1c, + 0x1c, + 0x1c, +}; +const uint8_t* const _I_Pin_arrow_up_7x9[] = {_I_Pin_arrow_up_7x9_0}; + +const uint8_t _I_Pin_attention_dpad_29x29_0[] = { + 0x01, 0x00, 0x56, 0x00, 0x80, 0x7f, 0x20, 0xe0, 0x31, 0x81, 0xc6, 0x20, 0x1c, 0x08, 0x05, + 0x82, 0x01, 0x20, 0xa0, 0x60, 0x20, 0x11, 0x0f, 0x04, 0x02, 0x03, 0x08, 0xf8, 0x40, 0x60, + 0x50, 0x4f, 0xc4, 0x0e, 0x09, 0x04, 0x05, 0x8c, 0x12, 0x04, 0x03, 0x18, 0x44, 0x08, 0x42, + 0x30, 0x88, 0x08, 0x0c, 0x62, 0x14, 0x18, 0x05, 0x02, 0x21, 0x61, 0x14, 0x8c, 0x43, 0xe3, + 0x01, 0xf8, 0x44, 0x7f, 0x20, 0x31, 0x89, 0x81, 0xcc, 0x1e, 0x61, 0x73, 0x0f, 0x98, 0x9c, + 0xc5, 0xe6, 0x37, 0x31, 0xf9, 0x91, 0xcc, 0x9e, 0x65, 0x73, 0x2f, 0x99, 0x9c, 0xcd, 0xe6, +}; +const uint8_t* const _I_Pin_attention_dpad_29x29[] = {_I_Pin_attention_dpad_29x29_0}; + +const uint8_t _I_Pin_back_arrow_10x8_0[] = { + 0x00, + 0x04, + 0x00, + 0x06, + 0x00, + 0xff, + 0x00, + 0x06, + 0x01, + 0x04, + 0x02, + 0x00, + 0x02, + 0x00, + 0x01, + 0xf8, + 0x00, +}; +const uint8_t* const _I_Pin_back_arrow_10x8[] = {_I_Pin_back_arrow_10x8_0}; + +const uint8_t _I_Pin_back_full_40x8_0[] = { + 0x01, 0x00, 0x26, 0x00, 0x82, 0x01, 0x0e, 0x0c, 0x02, 0x18, 0x14, 0x03, 0xfe, 0x04, + 0x38, 0x37, 0xc6, 0xc3, 0x32, 0xf7, 0x41, 0x20, 0x59, 0x0a, 0x54, 0xa6, 0x01, 0xf2, + 0x88, 0xde, 0x80, 0x83, 0x01, 0xc8, 0x42, 0xa5, 0x3f, 0x88, 0x05, 0x82, 0x65, 0x2e, +}; +const uint8_t* const _I_Pin_back_full_40x8[] = {_I_Pin_back_full_40x8_0}; + +const uint8_t _I_Pin_cell_13x13_0[] = { + 0x01, + 0x00, + 0x0a, + 0x00, + 0xff, + 0xc7, + 0xe0, + 0x31, + 0x00, + 0x0f, + 0x80, + 0x4c, + 0x2e, + 0x20, +}; +const uint8_t* const _I_Pin_cell_13x13[] = {_I_Pin_cell_13x13_0}; + +const uint8_t _I_Pin_pointer_5x3_0[] = { + 0x00, + 0x04, + 0x0e, + 0x1f, +}; +const uint8_t* const _I_Pin_pointer_5x3[] = {_I_Pin_pointer_5x3_0}; + +const uint8_t _I_Pin_star_7x7_0[] = { + 0x00, + 0x49, + 0x2a, + 0x1c, + 0x7f, + 0x1c, + 0x2a, + 0x49, +}; +const uint8_t* const _I_Pin_star_7x7[] = {_I_Pin_star_7x7_0}; + +const uint8_t _I_passport_bad1_46x49_0[] = { + 0x01, 0x00, 0xd2, 0x00, 0xff, 0x7f, 0xc0, 0x05, 0x1f, 0x02, 0x1f, 0xfe, 0x7e, 0x02, 0x18, 0x0f, + 0xe0, 0x0a, 0x57, 0xff, 0xf7, 0x9c, 0x0a, 0x59, 0xf8, 0x0e, 0x60, 0x0a, 0x56, 0xf8, 0x05, 0x83, + 0xfc, 0x05, 0x18, 0xbc, 0x03, 0x01, 0xfd, 0x02, 0x8c, 0x2c, 0x5a, 0x3f, 0xa0, 0x28, 0xc1, 0x40, + 0xa3, 0xf4, 0x02, 0x8c, 0x08, 0x0a, 0x77, 0xf8, 0x08, 0x14, 0x7d, 0x13, 0xfd, 0xf9, 0x14, 0x80, + 0xab, 0xd0, 0x9f, 0xd7, 0xe0, 0x10, 0x60, 0x2a, 0x42, 0x20, 0x1a, 0x09, 0xfc, 0xbe, 0x01, 0x10, + 0x02, 0xa5, 0x9c, 0x0a, 0x78, 0x0e, 0x74, 0x04, 0x0a, 0x31, 0x7a, 0x06, 0x7a, 0x06, 0x05, 0x39, + 0xb0, 0x44, 0x80, 0xa3, 0x7e, 0x02, 0xa5, 0xf0, 0x0a, 0x78, 0x0a, 0x00, 0x14, 0xf8, 0x13, 0xf0, + 0x29, 0xc8, 0x07, 0x66, 0x70, 0x11, 0xd8, 0xea, 0xa7, 0xf1, 0xb2, 0x99, 0x4c, 0x00, 0xa9, 0xc0, + 0x9f, 0x01, 0x4e, 0x01, 0x3d, 0x02, 0x8c, 0x38, 0x0a, 0x33, 0xa8, 0x6c, 0x02, 0x62, 0x05, 0x19, + 0xa0, 0x14, 0x78, 0x00, 0x51, 0x94, 0x01, 0x46, 0x01, 0x03, 0x02, 0xa4, 0x30, 0x0a, 0x2a, 0x02, + 0x98, 0x7c, 0x25, 0x60, 0x52, 0xe0, 0x43, 0xe5, 0x80, 0x51, 0xc0, 0x27, 0x46, 0x51, 0x09, 0x05, + 0x88, 0xc0, 0x66, 0x80, 0x52, 0xfe, 0x40, 0x27, 0x60, 0x52, 0xf8, 0x7f, 0xe7, 0xa0, 0x52, 0xe0, + 0x5f, 0xe7, 0xc0, 0x52, 0x80, 0x6f, 0xe7, 0xe0, 0x53, 0xde, 0x01, 0x50, 0xe2, 0x20, 0x5f, 0x02, + 0xbf, 0xfb, 0xfe, 0x00, 0x28, 0xf8, +}; +const uint8_t* const _I_passport_bad1_46x49[] = {_I_passport_bad1_46x49_0}; + +const uint8_t _I_passport_bad2_46x49_0[] = { + 0x01, 0x00, 0xee, 0x00, 0xff, 0x7f, 0xc0, 0x05, 0x1f, 0x02, 0x1f, 0xfe, 0x7e, 0x02, 0x18, 0x0f, + 0xe0, 0x0a, 0x57, 0xff, 0xf7, 0x9c, 0x0a, 0x59, 0xf8, 0x0e, 0x60, 0x0a, 0x5e, 0xf8, 0xfd, 0x83, + 0xfc, 0x05, 0x18, 0xbd, 0x83, 0x01, 0xfd, 0x02, 0x8c, 0x2f, 0x01, 0x01, 0xfd, 0x01, 0x46, 0x0b, + 0x00, 0x81, 0x7d, 0x00, 0xa3, 0x02, 0x80, 0x41, 0x3d, 0x13, 0xfb, 0xfc, 0x04, 0x0a, 0x3d, 0x09, + 0xfe, 0xfc, 0x88, 0x30, 0x80, 0x2a, 0xea, 0xa7, 0xf5, 0xf8, 0x04, 0x7e, 0xa1, 0xb5, 0x02, 0x8f, + 0x02, 0xc1, 0xb8, 0xbf, 0x4f, 0xe5, 0xf2, 0x0e, 0x07, 0x53, 0x03, 0x3e, 0x02, 0x8e, 0x9e, 0x75, + 0x80, 0x02, 0x8e, 0x42, 0x9d, 0x05, 0xd1, 0x4f, 0xa2, 0xf5, 0x08, 0xf4, 0x0c, 0x0a, 0x73, 0x69, + 0x08, 0x14, 0xab, 0x17, 0xe0, 0x29, 0xd4, 0x2f, 0x80, 0x53, 0xcc, 0x50, 0x24, 0x22, 0x31, 0x8b, + 0xfc, 0x08, 0x62, 0x05, 0x29, 0x07, 0x32, 0x0f, 0x40, 0x9f, 0xc5, 0xe2, 0x13, 0x8f, 0xc5, 0xfe, + 0x7f, 0x1b, 0x4f, 0x90, 0x44, 0x40, 0xa7, 0x00, 0x9e, 0x81, 0x52, 0x75, 0x1d, 0x80, 0x43, 0x80, + 0xa3, 0x34, 0x86, 0xc0, 0x26, 0x20, 0x54, 0xe0, 0x01, 0x46, 0x51, 0x0b, 0x01, 0x8c, 0x0c, 0x0a, + 0x90, 0xc0, 0x2a, 0x4c, 0x3e, 0x12, 0xb0, 0x28, 0xcc, 0x38, 0x10, 0xf9, 0x64, 0x24, 0x3a, 0x29, + 0xd1, 0x94, 0x01, 0x47, 0x00, 0x30, 0x19, 0xa0, 0x14, 0x60, 0x1f, 0xd8, 0x04, 0xec, 0x0a, 0x5f, + 0xaf, 0xfc, 0xf4, 0x0a, 0x5f, 0x4b, 0xfc, 0xf8, 0x0a, 0x5a, 0x8d, 0xfc, 0xfc, 0x0a, 0x54, 0x00, + 0x2a, 0x60, 0x37, 0x40, 0x53, 0x80, 0x2e, 0x44, 0x0a, 0x7a, 0x00, 0x2e, 0x7f, 0xbf, 0xe0, 0x02, + 0x8f, 0x80, +}; +const uint8_t* const _I_passport_bad2_46x49[] = {_I_passport_bad2_46x49_0}; + +const uint8_t _I_passport_bad3_46x49_0[] = { + 0x01, 0x00, 0x07, 0x01, 0xff, 0x7f, 0xc0, 0x05, 0x1f, 0x02, 0x1f, 0xfe, 0x7e, 0x02, 0x18, 0x0f, + 0xc0, 0x0a, 0x57, 0xff, 0xf7, 0xbc, 0x0a, 0x59, 0xf8, 0x0f, 0x40, 0x0a, 0x56, 0xf8, 0x04, 0xe0, + 0x0a, 0x51, 0x78, 0x07, 0x1b, 0xfc, 0x05, 0x18, 0x5c, 0x02, 0x03, 0xfd, 0x02, 0x8c, 0x37, 0x01, + 0x00, 0xfd, 0x01, 0x46, 0x15, 0x40, 0x80, 0x7d, 0x27, 0xf7, 0xf8, 0x48, 0x14, 0xf7, 0xf0, 0x80, + 0x28, 0xfa, 0x00, 0xa5, 0x20, 0x80, 0x72, 0x27, 0xf5, 0xf8, 0x80, 0x14, 0x76, 0x00, 0x52, 0x9f, + 0xc0, 0x2f, 0xd3, 0xf9, 0x7e, 0x82, 0x81, 0xc0, 0xc8, 0xcf, 0xa5, 0xf6, 0x0d, 0x3c, 0xe3, 0x20, + 0x05, 0x1d, 0x05, 0x32, 0x4b, 0xa0, 0x9f, 0x45, 0xec, 0x11, 0xc9, 0x18, 0x14, 0xe6, 0x94, 0x10, + 0x29, 0xd7, 0x00, 0xa9, 0x62, 0x02, 0x9f, 0x02, 0x83, 0x41, 0x11, 0x88, 0x14, 0x77, 0xf2, 0x00, + 0x29, 0x48, 0x39, 0x92, 0x7a, 0x84, 0xfe, 0x27, 0x10, 0x9c, 0x7e, 0x2f, 0xf3, 0xf8, 0xea, 0x78, + 0x68, 0x18, 0x09, 0xf4, 0x7c, 0x0a, 0x27, 0x21, 0x9e, 0xc6, 0xd5, 0x65, 0x01, 0x9d, 0x44, 0xe0, + 0x10, 0xe8, 0x04, 0x0a, 0x69, 0x63, 0x80, 0x4c, 0x60, 0x10, 0x49, 0xa6, 0x0e, 0x03, 0xc0, 0x80, + 0x42, 0x25, 0x10, 0x38, 0x34, 0x02, 0x06, 0x05, 0x28, 0x44, 0x02, 0x19, 0x10, 0x02, 0x8c, 0x42, + 0x01, 0x30, 0xf8, 0x4b, 0xe0, 0x71, 0x48, 0x07, 0x02, 0x3f, 0x2c, 0x05, 0x8e, 0x02, 0x03, 0x00, + 0x94, 0x43, 0xc2, 0x22, 0x30, 0x19, 0xa5, 0xc4, 0x0a, 0x3f, 0xc8, 0x04, 0xef, 0x02, 0x3c, 0x16, + 0xe8, 0xcf, 0x60, 0x31, 0xc0, 0xe8, 0xdf, 0xe7, 0xd0, 0x1b, 0x01, 0x34, 0x77, 0xf3, 0xf8, 0x08, + 0x88, 0xb7, 0x80, 0x51, 0x80, 0x10, 0x87, 0x40, 0x05, 0x22, 0x10, 0xd8, 0x00, 0xa5, 0x0a, 0x05, + 0x88, 0x74, 0x41, 0x64, 0x05, 0x7f, 0xf7, 0xfc, 0x00, 0x51, 0xf0, +}; +const uint8_t* const _I_passport_bad3_46x49[] = {_I_passport_bad3_46x49_0}; + +const uint8_t _I_passport_bottom_128x18_0[] = { + 0x01, 0x00, 0x54, 0x00, 0x99, 0x01, 0x97, 0xf1, 0xff, 0x00, 0x2e, 0x1c, 0x1e, 0xdf, 0xc0, + 0x7b, 0x63, 0xe6, 0xc0, 0xfe, 0x9e, 0x03, 0xfa, 0x70, 0x0f, 0xe9, 0x80, 0x7f, 0xc1, 0xfd, + 0x04, 0x37, 0xf7, 0xc9, 0x1d, 0xb8, 0x08, 0x4c, 0x04, 0x1f, 0xb0, 0x58, 0x10, 0x3f, 0x38, + 0x00, 0xfe, 0xb0, 0x41, 0x7e, 0x44, 0x96, 0x00, 0x2c, 0xfe, 0x0b, 0xfa, 0x07, 0xe4, 0x7e, + 0x13, 0x79, 0x1d, 0xce, 0x02, 0x03, 0xc0, 0x80, 0x7c, 0xf9, 0x83, 0xb9, 0x80, 0x40, 0xc0, + 0x43, 0x06, 0xc3, 0x0e, 0xe6, 0x01, 0xfe, 0x01, 0x0f, 0xf2, 0x06, 0x90, 0xd0, +}; +const uint8_t* const _I_passport_bottom_128x18[] = {_I_passport_bottom_128x18_0}; + +const uint8_t _I_passport_happy1_46x49_0[] = { + 0x01, 0x00, 0x09, 0x01, 0xff, 0x7f, 0xc0, 0x05, 0x1f, 0x02, 0x1f, 0xfe, 0x7f, 0xff, 0x87, 0xe0, + 0x00, 0xa7, 0xf1, 0xbf, 0x85, 0x04, 0x0a, 0x30, 0xec, 0x07, 0xe4, 0x0a, 0x37, 0xf8, 0x0c, 0x03, + 0xec, 0x05, 0x1d, 0xf8, 0x98, 0x7d, 0x00, 0x51, 0xaf, 0x81, 0x47, 0xa0, 0x05, 0x19, 0x78, 0x14, + 0xa0, 0x73, 0xf8, 0xb8, 0x14, 0x74, 0x1f, 0xc9, 0xf0, 0x14, 0xa4, 0x10, 0x39, 0xec, 0x2c, 0x0a, + 0x3c, 0x08, 0x04, 0xe8, 0x0a, 0x52, 0x00, 0x28, 0xc1, 0x7c, 0x10, 0x08, 0x87, 0x82, 0x77, 0x05, + 0xfc, 0x40, 0xe1, 0x1f, 0x80, 0x28, 0xff, 0x20, 0x70, 0x4f, 0xe4, 0xf6, 0x07, 0xfe, 0x80, 0xc0, + 0xbf, 0xd3, 0xe8, 0x1e, 0x7a, 0x0f, 0x00, 0xbf, 0xcf, 0xe0, 0x74, 0xe8, 0x46, 0x03, 0x7e, 0x05, + 0x19, 0x70, 0xbc, 0x7b, 0xe0, 0x51, 0x8a, 0x40, 0x3c, 0x1e, 0xf0, 0x28, 0xc4, 0x20, 0x1f, 0x0f, + 0xb8, 0x14, 0xff, 0x1f, 0xb9, 0xf9, 0xa8, 0x60, 0x3f, 0xcf, 0xc8, 0x14, 0xff, 0xde, 0x70, 0x29, + 0x61, 0xb0, 0xf9, 0xf0, 0x29, 0x12, 0x06, 0xfd, 0x3e, 0x02, 0x8f, 0x82, 0x0f, 0xf8, 0x9c, 0x81, + 0x44, 0x80, 0x3e, 0x09, 0xb8, 0x14, 0x94, 0x43, 0x2b, 0x80, 0xcc, 0x20, 0xc0, 0x71, 0x94, 0x40, + 0x69, 0x10, 0x90, 0x29, 0xe2, 0x21, 0x00, 0x51, 0x9b, 0x01, 0x4f, 0xc0, 0x23, 0x1c, 0x24, 0x43, + 0xf5, 0x1f, 0x17, 0x88, 0x14, 0x7e, 0x1e, 0x31, 0xd8, 0xe0, 0xa4, 0x18, 0x02, 0x99, 0x01, 0x46, + 0x01, 0xfa, 0x02, 0x8e, 0x06, 0x80, 0x05, 0x6f, 0xa4, 0xff, 0x03, 0x80, 0xc0, 0x01, 0x4a, 0x82, + 0x04, 0x18, 0x08, 0x14, 0xb8, 0x10, 0x18, 0x0f, 0xa2, 0x7f, 0x21, 0x02, 0x8c, 0x08, 0x0a, 0x31, + 0x10, 0x28, 0xc1, 0x3a, 0x13, 0xf8, 0x6f, 0x82, 0x07, 0x18, 0x02, 0x8c, 0x0c, 0x0a, 0x3e, 0x0d, + 0x00, 0xbc, 0x7e, 0x0b, 0x31, 0xb3, 0xcf, 0xff, 0xdf, 0xf0, 0x01, 0x47, 0xc0, +}; +const uint8_t* const _I_passport_happy1_46x49[] = {_I_passport_happy1_46x49_0}; + +const uint8_t _I_passport_happy2_46x49_0[] = { + 0x01, 0x00, 0x16, 0x01, 0xff, 0x7f, 0xc0, 0x05, 0x1f, 0x02, 0x1f, 0xfe, 0x7f, 0xff, 0x87, 0xe0, + 0x00, 0xa7, 0xf1, 0xbf, 0x85, 0x04, 0x0a, 0x30, 0xec, 0x07, 0x84, 0x0a, 0x37, 0xf8, 0x0c, 0x03, + 0xbe, 0x05, 0x1d, 0xfc, 0xfb, 0x81, 0xa0, 0x02, 0x8f, 0x7f, 0x83, 0x21, 0xa4, 0x43, 0xe7, 0xf2, + 0xf8, 0x06, 0x4a, 0xa1, 0xf3, 0x9f, 0x45, 0xe0, 0x10, 0xcc, 0x8c, 0x32, 0x7b, 0x17, 0xb8, 0x42, + 0x30, 0x50, 0x39, 0xd4, 0x2f, 0x19, 0x25, 0xe1, 0x40, 0x26, 0x90, 0xb8, 0x15, 0x1a, 0x40, 0x05, + 0x18, 0x2f, 0x86, 0x89, 0x18, 0xf0, 0x4d, 0xe0, 0xbf, 0x98, 0x2c, 0x13, 0xf1, 0x3b, 0xc2, 0xff, + 0x20, 0xb0, 0x2f, 0xe4, 0xf7, 0x07, 0xfe, 0x82, 0xc0, 0x7f, 0xd3, 0xec, 0x1e, 0x7b, 0x8f, 0x00, + 0xbf, 0xcf, 0xf0, 0x74, 0xc9, 0xc6, 0x03, 0x7f, 0x3f, 0x81, 0xc8, 0x60, 0x1c, 0x0e, 0xf8, 0x14, + 0x62, 0xd0, 0x0f, 0x07, 0xbc, 0x0a, 0x31, 0x88, 0x07, 0xc3, 0xee, 0x05, 0x18, 0x84, 0x03, 0xf1, + 0xfb, 0x9f, 0x9a, 0x86, 0x03, 0xfc, 0xfc, 0x81, 0x4f, 0xfd, 0xe7, 0x02, 0x96, 0x1b, 0x0f, 0x9f, + 0x02, 0x97, 0xe2, 0x07, 0x7e, 0x9f, 0x01, 0x47, 0xc1, 0x07, 0xfc, 0x4c, 0x40, 0xa2, 0x40, 0x1f, + 0x04, 0xbc, 0x0a, 0x4a, 0x21, 0x95, 0xc0, 0x66, 0x10, 0x60, 0x38, 0xca, 0x20, 0x34, 0x88, 0x48, + 0x14, 0xf1, 0x10, 0x80, 0x28, 0xcd, 0x80, 0xa7, 0xe0, 0x9c, 0xc0, 0x70, 0x91, 0x0f, 0xd4, 0x7c, + 0x5e, 0x20, 0x51, 0xf8, 0x78, 0xc7, 0x63, 0x82, 0x90, 0x60, 0x0a, 0x64, 0x05, 0x18, 0x07, 0xe8, + 0x0a, 0x38, 0x1a, 0x00, 0x15, 0xbe, 0x93, 0xfc, 0x0e, 0x03, 0x00, 0x05, 0x2a, 0x08, 0x10, 0x60, + 0x20, 0x52, 0xe0, 0x40, 0x60, 0x3e, 0x89, 0xfc, 0x84, 0x0a, 0x30, 0x20, 0x28, 0xc4, 0x40, 0xa3, + 0x04, 0xe8, 0x4f, 0xe1, 0xbe, 0x04, 0x88, 0x81, 0x46, 0x06, 0x05, 0x1f, 0x06, 0x80, 0x5e, 0x3f, + 0x08, 0x3c, 0x02, 0xcf, 0x3f, 0xff, 0x7f, 0xc0, 0x05, 0x1f, +}; +const uint8_t* const _I_passport_happy2_46x49[] = {_I_passport_happy2_46x49_0}; + +const uint8_t _I_passport_happy3_46x49_0[] = { + 0x01, 0x00, 0x23, 0x01, 0xff, 0x7f, 0xc0, 0x05, 0x1f, 0x02, 0x1f, 0xfe, 0x7f, 0xff, 0x81, 0xe0, + 0x00, 0xa7, 0xfc, 0xbf, 0xff, 0x00, 0xa3, 0x7f, 0x85, 0xe0, 0x3e, 0x60, 0x51, 0xdf, 0xc1, 0x38, + 0x1e, 0xc0, 0x28, 0xd7, 0xe0, 0x52, 0x0e, 0x98, 0x14, 0x65, 0xf0, 0x28, 0x86, 0x92, 0x1f, 0x3f, + 0x8b, 0xc0, 0xa1, 0x14, 0x8f, 0x9c, 0xfa, 0x2f, 0x04, 0x84, 0x64, 0x21, 0x93, 0xd8, 0x5f, 0xf2, + 0x6b, 0xa0, 0x81, 0xce, 0xa1, 0x70, 0x2a, 0x37, 0x82, 0x05, 0x34, 0x82, 0xfe, 0x70, 0x92, 0x08, + 0x24, 0xd3, 0x1b, 0x04, 0x82, 0xc4, 0x7d, 0x13, 0x7c, 0xbf, 0xca, 0x0b, 0x0d, 0xfc, 0x4e, 0xf4, + 0x7f, 0xa8, 0x2c, 0x0f, 0xf9, 0x3d, 0xe1, 0xe7, 0xa0, 0xf0, 0x1f, 0xf4, 0xfb, 0x07, 0x8e, 0xe3, + 0xc0, 0x2f, 0xf3, 0xfc, 0x0d, 0xd2, 0x71, 0x80, 0xdf, 0x81, 0x46, 0x73, 0x00, 0xe0, 0x77, 0xe7, + 0xf0, 0x18, 0x9c, 0x03, 0xc1, 0xef, 0x02, 0x8c, 0x7a, 0x01, 0xf0, 0xfb, 0x81, 0x46, 0x21, 0x00, + 0xfc, 0x7e, 0xe7, 0xf0, 0x38, 0x04, 0x07, 0xf9, 0xf9, 0x9f, 0xc1, 0x40, 0xa3, 0xfe, 0xf3, 0xcf, + 0xe1, 0x30, 0x0c, 0x36, 0x1f, 0x3f, 0x3f, 0x88, 0x85, 0x86, 0x07, 0x7e, 0x9f, 0x48, 0x45, 0x03, + 0x07, 0xfc, 0x4c, 0x68, 0x2a, 0xa1, 0xbf, 0xf8, 0x25, 0xf4, 0x30, 0x28, 0xa8, 0x86, 0x57, 0x47, + 0x98, 0x41, 0x80, 0xe3, 0x28, 0x96, 0xd2, 0x04, 0xa2, 0x05, 0x18, 0xce, 0x22, 0x10, 0x05, 0x19, + 0xb4, 0xc4, 0x0a, 0x5e, 0x09, 0xcd, 0x87, 0x09, 0x10, 0xfd, 0x47, 0xe7, 0xdc, 0x10, 0x28, 0xfc, + 0x3c, 0x66, 0x51, 0xc1, 0x48, 0x30, 0x05, 0x31, 0x02, 0x94, 0x03, 0xf4, 0x05, 0x1c, 0x0d, 0x00, + 0x0a, 0xdf, 0x40, 0x28, 0xe0, 0x30, 0x00, 0x52, 0xa0, 0x81, 0x06, 0x02, 0x05, 0x2e, 0x04, 0x06, + 0x03, 0xe8, 0x9f, 0xc8, 0x40, 0xa3, 0x02, 0x02, 0x8c, 0x44, 0x0a, 0x30, 0x4e, 0x84, 0xfe, 0x1b, + 0xe0, 0x81, 0xc6, 0x00, 0xa3, 0x03, 0x02, 0x8f, 0x83, 0x40, 0x95, 0x1f, 0x84, 0x1e, 0x01, 0x67, + 0x9f, 0xff, 0xbf, 0xe0, 0x02, 0x8f, 0x80, +}; +const uint8_t* const _I_passport_happy3_46x49[] = {_I_passport_happy3_46x49_0}; + +const uint8_t _I_passport_left_6x46_0[] = { + 0x01, 0x00, 0x1b, 0x00, 0x9e, 0x40, 0xa3, 0x32, 0x59, 0x2c, 0x66, 0x03, 0x01, 0x82, 0xc2, 0x62, + 0x32, 0x50, 0x16, 0xc8, 0x60, 0x30, 0x28, 0x34, 0x3a, 0x3d, 0x3e, 0x9d, 0x4c, 0x80, 0x14, +}; +const uint8_t* const _I_passport_left_6x46[] = {_I_passport_left_6x46_0}; + +const uint8_t _I_passport_okay1_46x49_0[] = { + 0x01, 0x00, 0xcc, 0x00, 0xff, 0x7f, 0xc0, 0x05, 0x1f, 0x02, 0x1f, 0xfe, 0x7e, 0x02, 0x1b, 0xfc, + 0x00, 0x0a, 0x78, 0xff, 0xff, 0xe0, 0x0a, 0x57, 0x38, 0x07, 0x9c, 0x0a, 0x50, 0xc8, 0x06, 0x60, + 0x0a, 0x37, 0xf8, 0x1c, 0x02, 0xc0, 0x05, 0x1d, 0xf8, 0xb4, 0x70, 0x13, 0xef, 0xfd, 0x7c, 0x68, + 0x53, 0xdf, 0xfc, 0xbc, 0x0a, 0x53, 0xaf, 0xfc, 0x5c, 0x0b, 0x13, 0x4f, 0xfc, 0x2c, 0x0b, 0x12, + 0x8f, 0xfc, 0x14, 0x0a, 0xdf, 0x08, 0x0c, 0xc3, 0xff, 0x02, 0x80, 0x7a, 0x20, 0x60, 0x53, 0xfa, + 0x41, 0xc0, 0xa7, 0x12, 0x87, 0xc8, 0x00, 0xa5, 0x92, 0x02, 0xa7, 0xc8, 0x0b, 0x5e, 0x28, 0x58, + 0x14, 0xe0, 0x90, 0xc0, 0x29, 0xfa, 0x20, 0xe0, 0x51, 0x1d, 0x8c, 0x42, 0x10, 0x05, 0x38, 0x44, + 0x40, 0x0a, 0x38, 0x58, 0x78, 0x30, 0x40, 0xa3, 0x7d, 0x29, 0x94, 0x82, 0xff, 0x06, 0x02, 0x9e, + 0x7e, 0x02, 0x88, 0x10, 0x28, 0xdb, 0xd1, 0xc4, 0x05, 0x13, 0xe1, 0x50, 0x00, 0xa2, 0x76, 0x29, + 0x00, 0x15, 0x22, 0x00, 0x51, 0x3e, 0x14, 0x38, 0x0a, 0x7c, 0x01, 0x28, 0xc8, 0x3c, 0xb0, 0xf9, + 0xe0, 0x64, 0xa3, 0x7f, 0x05, 0xf8, 0x8a, 0x50, 0x0a, 0x4b, 0x83, 0x02, 0x8f, 0x7e, 0x01, 0xe0, + 0x2a, 0x0c, 0x81, 0xbc, 0x41, 0x81, 0x2c, 0x8f, 0x83, 0xfe, 0x11, 0x2f, 0xff, 0xbe, 0x3e, 0x05, + 0x40, 0xb0, 0x81, 0x4c, 0x74, 0x20, 0x52, 0x15, 0x1c, 0x83, 0xfc, 0x23, 0x10, 0x00, 0xc7, 0xc0, +}; +const uint8_t* const _I_passport_okay1_46x49[] = {_I_passport_okay1_46x49_0}; + +const uint8_t _I_passport_okay2_46x49_0[] = { + 0x01, 0x00, 0xe5, 0x00, 0xff, 0x7f, 0xc0, 0x05, 0x1f, 0x02, 0x1f, 0xfe, 0x7e, 0x02, 0x1b, 0xfe, + 0x00, 0x0a, 0x78, 0x7b, 0xff, 0xf0, 0x0a, 0x57, 0x9c, 0x77, 0x8c, 0x0a, 0x37, 0xfc, 0x34, 0x07, + 0x38, 0x05, 0x1d, 0xfd, 0x06, 0x01, 0x60, 0x02, 0x8d, 0x7e, 0x41, 0x00, 0xc0, 0x4f, 0xbf, 0xf2, + 0xf8, 0x80, 0xd0, 0x67, 0xbf, 0xf8, 0xb8, 0x14, 0xa7, 0x40, 0x51, 0x84, 0x01, 0x4e, 0x17, 0x0c, + 0x02, 0x8c, 0xd3, 0xff, 0x05, 0x82, 0x01, 0x5e, 0x51, 0xff, 0x81, 0x40, 0xbf, 0x10, 0x30, 0x29, + 0xc1, 0x20, 0x93, 0x00, 0x29, 0x7c, 0xa1, 0x20, 0x51, 0xff, 0x40, 0xfd, 0x31, 0x39, 0x85, 0xfe, + 0x03, 0x1c, 0x8a, 0xc4, 0xe4, 0x17, 0xf8, 0x2f, 0x83, 0x2b, 0x17, 0x90, 0x6f, 0xf0, 0x90, 0x0f, + 0xa8, 0x16, 0xbc, 0xa0, 0x52, 0x84, 0x40, 0x61, 0x51, 0x20, 0x29, 0xfd, 0xa3, 0xe0, 0x52, 0x80, + 0x46, 0xa1, 0x02, 0x91, 0x80, 0xf8, 0x21, 0x31, 0x00, 0x28, 0xe0, 0x63, 0xf0, 0x80, 0x28, 0xff, + 0xef, 0xca, 0xc2, 0x90, 0x4f, 0xe0, 0x68, 0x21, 0x02, 0x8f, 0x7c, 0x12, 0x20, 0x52, 0x97, 0x81, + 0x52, 0x2e, 0x05, 0x1a, 0x00, 0x14, 0x61, 0x61, 0xb2, 0x00, 0x8c, 0x14, 0x0a, 0x31, 0x80, 0x2a, + 0x41, 0x80, 0xa7, 0xc0, 0x80, 0x81, 0x47, 0xcb, 0x03, 0x9e, 0x06, 0x4a, 0x37, 0xfc, 0x1b, 0x08, + 0xa5, 0x00, 0xa4, 0x35, 0x20, 0x29, 0x10, 0x47, 0xc1, 0x0f, 0x26, 0x93, 0x90, 0x43, 0x02, 0x59, + 0x1f, 0x07, 0xfc, 0x22, 0x5f, 0xff, 0x7c, 0x7c, 0x0a, 0x81, 0x61, 0x02, 0x98, 0xe8, 0x40, 0xa4, + 0x2a, 0x39, 0x07, 0xf8, 0x46, 0x20, 0x01, 0x8f, 0x80, +}; +const uint8_t* const _I_passport_okay2_46x49[] = {_I_passport_okay2_46x49_0}; + +const uint8_t _I_passport_okay3_46x49_0[] = { + 0x01, 0x00, 0x06, 0x01, 0xff, 0x7f, 0xc0, 0x05, 0x1f, 0x02, 0x1f, 0xfe, 0x7e, 0x02, 0x2c, 0x00, + 0x14, 0xfb, 0xf7, 0xff, 0xe0, 0x14, 0xa4, 0xf8, 0x0f, 0x18, 0x14, 0xaf, 0x30, 0x0c, 0xe0, 0x14, + 0x6f, 0xf8, 0x68, 0x05, 0xa0, 0x0a, 0x3b, 0xf8, 0x0c, 0x07, 0x11, 0x3e, 0xff, 0xd7, 0xe0, 0x10, + 0x28, 0x44, 0xf7, 0xff, 0x2f, 0x02, 0x8c, 0x12, 0x75, 0xff, 0x8b, 0xc0, 0x20, 0x80, 0x52, 0x85, + 0x81, 0x4a, 0x68, 0x05, 0x28, 0x44, 0x08, 0x0a, 0x30, 0x50, 0x29, 0x4a, 0x00, 0xa5, 0xfc, 0x81, + 0x81, 0x4e, 0x05, 0x06, 0x98, 0x01, 0x4b, 0xf3, 0x04, 0x02, 0x8f, 0xfb, 0x07, 0x04, 0x84, 0xcc, + 0x2f, 0xf0, 0x1c, 0xee, 0x2a, 0x15, 0x28, 0x02, 0x8f, 0x86, 0xe4, 0x05, 0x1d, 0xfe, 0x03, 0x01, + 0x52, 0x02, 0xa0, 0x2c, 0x64, 0x80, 0x52, 0xc5, 0x43, 0x80, 0xa7, 0x07, 0x87, 0x81, 0x4a, 0x01, + 0xff, 0x83, 0xc8, 0xb7, 0xf0, 0x08, 0x0c, 0x3a, 0x09, 0x22, 0x14, 0x94, 0x16, 0x11, 0x21, 0xbf, + 0xe0, 0x6f, 0xf0, 0x40, 0x28, 0xff, 0xef, 0xd1, 0x45, 0x60, 0xc8, 0x67, 0xf0, 0x38, 0x58, 0x7c, + 0x64, 0x5d, 0xfe, 0x04, 0x18, 0x0a, 0x33, 0xc9, 0x7e, 0x82, 0x03, 0x40, 0x80, 0x48, 0x22, 0xf5, + 0x08, 0x00, 0x14, 0xa1, 0x60, 0x51, 0x90, 0x40, 0x26, 0x10, 0x59, 0x44, 0x02, 0x21, 0x00, 0x94, + 0x01, 0x4a, 0x1d, 0x00, 0x92, 0x01, 0x47, 0x81, 0x01, 0x02, 0x8f, 0x96, 0x57, 0x3c, 0x1a, 0x8c, + 0x8a, 0x36, 0x8d, 0x10, 0x29, 0x2b, 0x04, 0x00, 0x52, 0x15, 0xc0, 0x80, 0x07, 0x00, 0x41, 0x18, + 0x07, 0x82, 0x1f, 0x80, 0x92, 0x37, 0x88, 0x30, 0x32, 0x9f, 0xff, 0x83, 0xfe, 0x12, 0x19, 0x97, + 0xff, 0xdf, 0x1f, 0x02, 0x8c, 0x90, 0x0a, 0x30, 0xf0, 0x28, 0xae, 0x47, 0xde, 0x3a, 0x12, 0x68, + 0xb8, 0xc8, 0x00, 0x32, 0x0f, 0xf0, 0x8c, 0x40, 0x03, 0x1f, +}; +const uint8_t* const _I_passport_okay3_46x49[] = {_I_passport_okay3_46x49_0}; + +const uint8_t _I_BatteryBody_52x28_0[] = { + 0x01, 0x00, 0x45, 0x00, 0xe0, 0x7f, 0x3f, 0xe0, 0x02, 0x87, 0xf0, 0x21, 0xe0, 0xc3, 0x84, + 0x50, 0x39, 0xbf, 0xff, 0x27, 0xfe, 0xf3, 0x09, 0xe0, 0x42, 0x81, 0xab, 0x0d, 0x03, 0x1c, + 0x2b, 0xfc, 0x0d, 0x48, 0x55, 0xdc, 0x1a, 0x90, 0x8f, 0x18, 0x6d, 0x41, 0xaa, 0x1b, 0x71, + 0x4b, 0x0d, 0xd4, 0x1b, 0xe0, 0xdf, 0x1b, 0xd5, 0xfc, 0x1a, 0xa5, 0x36, 0x06, 0xac, 0x20, + 0xa7, 0xe0, 0xdc, 0xa5, 0x7c, 0x7c, 0xb7, 0xff, 0xb4, 0x21, 0x5c, 0xcb, 0xc6, +}; +const uint8_t* const _I_BatteryBody_52x28[] = {_I_BatteryBody_52x28_0}; + +const uint8_t _I_Battery_16x16_0[] = { + 0x01, 0x00, 0x12, 0x00, 0x00, 0x1e, 0x02, 0x03, 0xc0, 0x81, 0xc8, + 0x20, 0x80, 0x11, 0xd0, 0x41, 0x40, 0x72, 0x11, 0x10, 0xda, 0x80, +}; +const uint8_t* const _I_Battery_16x16[] = {_I_Battery_16x16_0}; + +const uint8_t _I_FaceCharging_29x14_0[] = { + 0x01, 0x00, 0x28, 0x00, 0xa0, 0x00, 0x86, 0x05, 0x60, 0x01, 0x8c, 0x0e, 0x61, 0x00, 0xc0, + 0x40, 0x63, 0x10, 0x0e, 0x04, 0x03, 0xf9, 0x00, 0xf0, 0x41, 0xc0, 0x66, 0x13, 0xb8, 0x40, + 0x94, 0xc0, 0x07, 0x04, 0x82, 0x00, 0xc6, 0x11, 0x02, 0x01, 0x8f, 0xc2, 0x03, 0x00, +}; +const uint8_t* const _I_FaceCharging_29x14[] = {_I_FaceCharging_29x14_0}; + +const uint8_t _I_FaceConfused_29x14_0[] = { + 0x01, 0x00, 0x30, 0x00, 0xc0, 0x00, 0x46, 0x1f, 0x38, 0x80, 0xd0, 0x22, 0x14, + 0x48, 0x0c, 0x82, 0x0f, 0x52, 0x80, 0xe8, 0x21, 0x14, 0xa0, 0x18, 0xc2, 0xa6, + 0x59, 0x19, 0x24, 0x27, 0x09, 0x48, 0xa1, 0x41, 0x2f, 0x12, 0x4c, 0x0c, 0x0c, + 0x51, 0x1f, 0xc8, 0x78, 0x0c, 0x7f, 0xd1, 0xf0, 0x18, 0xc3, 0xa3, 0x00, 0x74, +}; +const uint8_t* const _I_FaceConfused_29x14[] = {_I_FaceConfused_29x14_0}; + +const uint8_t _I_FaceNopower_29x14_0[] = { + 0x01, 0x00, 0x24, 0x00, 0x00, 0x1f, 0x02, 0x01, 0x60, 0x01, 0xa7, 0x80, 0x02, 0x57, + 0xe0, 0x48, 0xc3, 0xe7, 0xd0, 0x0c, 0x04, 0x3c, 0x39, 0x1f, 0x88, 0x18, 0x0c, 0x61, + 0x90, 0x60, 0x18, 0xff, 0x82, 0x44, 0x03, 0x38, 0x74, 0x38, 0x2c, 0x80, +}; +const uint8_t* const _I_FaceNopower_29x14[] = {_I_FaceNopower_29x14_0}; + +const uint8_t _I_FaceNormal_29x14_0[] = { + 0x01, 0x00, 0x1e, 0x00, 0x00, 0x1c, 0xf2, 0x01, 0x80, 0x83, 0xd7, 0xa0, + 0x1c, 0x08, 0x5d, 0xf8, 0x06, 0x30, 0xf0, 0x1b, 0x84, 0xcc, 0x41, 0x10, + 0x88, 0x10, 0x0e, 0x62, 0x10, 0x10, 0x18, 0xf8, 0x00, 0x42, +}; +const uint8_t* const _I_FaceNormal_29x14[] = {_I_FaceNormal_29x14_0}; + +const uint8_t _I_Health_16x16_0[] = { + 0x01, 0x00, 0x12, 0x00, 0x00, 0x2f, 0x02, 0x03, 0x40, 0x00, 0x95, + 0xe2, 0x1f, 0x08, 0x84, 0x00, 0xc4, 0x12, 0x60, 0xf1, 0x0c, 0xb8, +}; +const uint8_t* const _I_Health_16x16[] = {_I_Health_16x16_0}; + +const uint8_t _I_Temperature_16x16_0[] = { + 0x01, 0x00, 0x12, 0x00, 0x00, 0x1e, 0x02, 0x01, 0x40, 0x80, 0x80, + 0x66, 0x41, 0x02, 0xf0, 0x40, 0xc0, 0x23, 0xc0, 0x80, 0x86, 0xd4, +}; +const uint8_t* const _I_Temperature_16x16[] = {_I_Temperature_16x16_0}; + +const uint8_t _I_Unplug_bg_bottom_128x10_0[] = { + 0x01, 0x00, 0x27, 0x00, 0xff, 0x7f, 0xc0, 0x19, 0x7f, 0x07, 0xf8, 0x3f, 0xaf, 0x7f, 0xc0, + 0x7e, 0xcf, 0x41, 0xfd, 0x3e, 0xfc, 0x03, 0xb2, 0x7f, 0x1f, 0xf8, 0x03, 0xf3, 0x8f, 0x0f, + 0xf0, 0x0b, 0xf6, 0x0f, 0x80, 0x07, 0xec, 0x06, 0x00, 0x50, 0xc9, 0xfc, 0x00, +}; +const uint8_t* const _I_Unplug_bg_bottom_128x10[] = {_I_Unplug_bg_bottom_128x10_0}; + +const uint8_t _I_Unplug_bg_top_128x14_0[] = { + 0x01, 0x00, 0x4f, 0x00, 0x80, 0x7f, 0x3f, 0xe0, 0x0a, 0x9f, 0xc0, 0x30, 0x00, 0xeb, + 0x00, 0x0c, 0x07, 0xc0, 0x0f, 0xd8, 0x3f, 0xc3, 0x8e, 0x0f, 0xc8, 0x7f, 0xe3, 0xf2, + 0x0f, 0xc8, 0xff, 0xf3, 0xfc, 0x0d, 0xa8, 0x1f, 0xfa, 0x7f, 0x7c, 0x0e, 0xdf, 0xf3, + 0xf3, 0x3f, 0xdf, 0x03, 0xaa, 0xff, 0xff, 0xfb, 0xdf, 0xfb, 0xc0, 0xfa, 0xdf, 0xff, + 0xfc, 0x60, 0xfc, 0xef, 0xf3, 0xb0, 0x17, 0xff, 0x78, 0x3e, 0xab, 0xfa, 0x1e, 0x0f, + 0xfc, 0x1f, 0x71, 0xf8, 0x27, 0x89, 0x3b, 0xbd, 0x7e, 0x4a, 0x33, 0x59, 0xa4, +}; +const uint8_t* const _I_Unplug_bg_top_128x14[] = {_I_Unplug_bg_top_128x14_0}; + +const uint8_t _I_Voltage_16x16_0[] = { + 0x01, 0x00, 0x1a, 0x00, 0x00, 0x24, 0x0a, 0x01, 0x03, 0xc0, 0x40, 0x78, 0x10, 0x1f, 0x04, + 0x03, 0xe1, 0x07, 0xc0, 0x40, 0xc0, 0xe3, 0xc0, 0x80, 0x58, 0x20, 0x12, 0x00, 0xd3, 0x00, +}; +const uint8_t* const _I_Voltage_16x16[] = {_I_Voltage_16x16_0}; + +const uint8_t _I_RFIDDolphinReceive_97x61_0[] = { + 0x01, 0x00, 0x87, 0x01, 0x00, 0x0f, 0xfa, 0x3e, 0x04, 0x28, 0x08, 0x2d, 0x78, 0x10, 0x1f, 0x00, + 0x24, 0x70, 0x01, 0x86, 0x98, 0x00, 0x86, 0x0c, 0x0c, 0x88, 0x60, 0x08, 0x63, 0x10, 0x0a, 0x00, + 0x31, 0xa0, 0x40, 0x21, 0x90, 0x03, 0x04, 0x1a, 0x5a, 0x08, 0x50, 0xe9, 0x01, 0x23, 0x20, 0x07, + 0x88, 0x30, 0xc5, 0xa6, 0x03, 0x10, 0x61, 0xfc, 0x0a, 0xa2, 0x2d, 0x48, 0x0c, 0x82, 0x20, 0x04, + 0x18, 0x40, 0x40, 0x42, 0x44, 0x37, 0x28, 0x80, 0x30, 0xbc, 0x94, 0xd0, 0x62, 0x4f, 0x20, 0x91, + 0x08, 0x44, 0x12, 0x01, 0x17, 0xe6, 0x40, 0x42, 0x45, 0x00, 0xa1, 0x03, 0x08, 0xa8, 0x31, 0x41, + 0x88, 0x83, 0x0f, 0x03, 0x08, 0x06, 0x1c, 0x1f, 0xa1, 0x01, 0x84, 0x1f, 0x8a, 0x31, 0x09, 0x0c, + 0xa5, 0x40, 0x86, 0x30, 0x98, 0x46, 0x02, 0x48, 0x0c, 0x40, 0xc9, 0x61, 0x00, 0xe2, 0x0c, 0x18, + 0x88, 0x65, 0xb8, 0x85, 0x51, 0x06, 0x21, 0x34, 0x83, 0x23, 0x44, 0x06, 0x29, 0x1c, 0xb4, 0x94, + 0xf8, 0x05, 0x19, 0x12, 0x20, 0xc2, 0x40, 0xb4, 0xa8, 0x18, 0xa9, 0xb5, 0x9b, 0x48, 0x28, 0x05, + 0xa1, 0x06, 0x22, 0xd4, 0xa3, 0x7e, 0x05, 0x98, 0xe0, 0x62, 0x0c, 0xf6, 0x86, 0xf8, 0x16, 0x63, + 0x42, 0x06, 0x0b, 0xa1, 0x60, 0xfe, 0x06, 0xe8, 0xcf, 0x23, 0x0d, 0x53, 0x00, 0x14, 0x0f, 0xe0, + 0xea, 0x28, 0xa0, 0x31, 0xa0, 0x3f, 0x08, 0x18, 0x10, 0x45, 0xa2, 0x11, 0x20, 0x01, 0xf4, 0x3f, + 0xe0, 0x81, 0x84, 0x02, 0x94, 0x18, 0xb0, 0xc0, 0x63, 0xc6, 0x3f, 0xe0, 0x31, 0x87, 0x03, 0x1e, + 0x11, 0x3c, 0x80, 0x47, 0xc1, 0x91, 0x18, 0x80, 0x58, 0x30, 0x0e, 0x01, 0x00, 0x30, 0xbc, 0x47, + 0xc3, 0x05, 0x06, 0x3c, 0x52, 0x00, 0xe4, 0x20, 0xcc, 0x80, 0x04, 0x4d, 0x00, 0x83, 0x73, 0x08, + 0x01, 0x8f, 0xa2, 0x0c, 0xa1, 0xe1, 0xa0, 0x62, 0x16, 0x0c, 0xac, 0x04, 0x14, 0xd0, 0x30, 0x08, + 0x80, 0x31, 0xb8, 0x10, 0x27, 0x89, 0x03, 0x1e, 0x81, 0x05, 0xe0, 0x01, 0x04, 0x1e, 0x40, 0x04, + 0xd0, 0x1c, 0x85, 0x6a, 0x20, 0xc7, 0xa8, 0x02, 0x84, 0xd2, 0x34, 0x00, 0x63, 0x6c, 0x11, 0xe2, + 0x4b, 0x10, 0x63, 0xd6, 0x20, 0x16, 0xa9, 0x80, 0x32, 0x35, 0x90, 0x0e, 0xa5, 0x04, 0x19, 0x15, + 0x48, 0x06, 0xa3, 0x07, 0x01, 0x06, 0x3c, 0xa8, 0x84, 0x30, 0xf8, 0x10, 0x31, 0xe2, 0xa5, 0xc1, + 0x8f, 0x7f, 0x2b, 0xe9, 0xa8, 0xa0, 0x5f, 0x60, 0x04, 0x21, 0x00, 0x29, 0x98, 0x74, 0x1f, 0xa8, + 0x0a, 0x39, 0xc0, 0x05, 0xf5, 0x83, 0xb0, 0xa0, 0x00, 0x3e, 0xaf, 0xfc, 0x1c, 0x19, 0x3d, 0x01, + 0xfb, 0xaa, 0xd3, 0x3c, 0x0c, 0xaa, 0x06, 0x54, 0x19, 0x50, 0x0c, 0xd0, 0x32, 0xe2, 0x05, 0xf1, + 0x00, 0x4c, 0x20, 0x19, 0xe0, 0xc9, 0x7d, 0x08, 0x33, 0xc0, 0x04, +}; +const uint8_t* const _I_RFIDDolphinReceive_97x61[] = {_I_RFIDDolphinReceive_97x61_0}; + +const uint8_t _I_RFIDDolphinSend_97x61_0[] = { + 0x01, 0x00, 0x8d, 0x01, 0x00, 0x0f, 0xfa, 0x3e, 0x04, 0x2a, 0x00, 0x2d, 0x78, 0x10, 0x1f, 0x04, + 0x04, 0x0a, 0x38, 0x00, 0x62, 0xcc, 0x00, 0x43, 0x06, 0x06, 0x44, 0x30, 0x04, 0x31, 0x80, 0x31, + 0x07, 0x48, 0x00, 0x50, 0x20, 0x10, 0xc8, 0x01, 0x64, 0x0c, 0x1d, 0x04, 0x28, 0x24, 0x83, 0xd2, + 0x81, 0x04, 0xc4, 0x18, 0x42, 0xc3, 0x01, 0x90, 0x30, 0xbe, 0x05, 0x51, 0x29, 0xa0, 0x74, 0x60, + 0x80, 0xc1, 0x84, 0x0b, 0x44, 0x5e, 0x43, 0x73, 0x82, 0x41, 0x20, 0x1e, 0x4a, 0x68, 0x31, 0x27, + 0x90, 0x48, 0x84, 0x20, 0x18, 0x31, 0x7e, 0x64, 0x06, 0x20, 0x0c, 0x2a, 0x14, 0x12, 0x40, 0x0c, + 0x28, 0xa0, 0xc4, 0x41, 0x87, 0x81, 0x17, 0x08, 0x30, 0xa0, 0xfd, 0x08, 0x0c, 0x20, 0xfc, 0x38, + 0x08, 0xc4, 0x24, 0x32, 0x95, 0x02, 0x18, 0xc2, 0x61, 0x18, 0x09, 0x20, 0x31, 0x03, 0x25, 0x84, + 0x1d, 0x88, 0x30, 0x62, 0x21, 0x96, 0xe2, 0x44, 0x22, 0x00, 0xc2, 0x26, 0xa0, 0x64, 0x68, 0x80, + 0xc4, 0x33, 0x9e, 0x92, 0x9f, 0x00, 0xa3, 0x48, 0x24, 0x00, 0xc4, 0x40, 0xa4, 0xa8, 0x18, 0xa9, + 0xb5, 0x9b, 0x48, 0x28, 0x05, 0xa1, 0x06, 0x22, 0xd4, 0xa3, 0x7e, 0x05, 0x98, 0xe0, 0x4f, 0x22, + 0xcf, 0x58, 0x6f, 0x80, 0x10, 0x34, 0x24, 0x31, 0x3a, 0x52, 0x0f, 0xe0, 0x03, 0x0c, 0xf1, 0xee, + 0x2d, 0x63, 0x00, 0x0c, 0x0f, 0xe0, 0x13, 0x28, 0xa0, 0x31, 0xa0, 0x3f, 0x08, 0x18, 0x10, 0x45, + 0xa2, 0xe3, 0x40, 0x00, 0xf4, 0x3f, 0xe1, 0xa1, 0x84, 0x02, 0x94, 0x18, 0xb0, 0xc0, 0x63, 0xc6, + 0x3f, 0xe0, 0x31, 0x87, 0x03, 0x1e, 0x11, 0x3c, 0x80, 0x47, 0xc1, 0x90, 0x56, 0x1b, 0x06, 0x01, + 0xc0, 0x20, 0x06, 0x17, 0x88, 0xf8, 0x60, 0xa0, 0xc7, 0x31, 0x8a, 0x58, 0x60, 0xe1, 0x99, 0x00, + 0x08, 0x9a, 0x01, 0x06, 0xd9, 0x10, 0x03, 0x1f, 0x44, 0x19, 0x43, 0xc3, 0x40, 0xc4, 0x2c, 0x19, + 0x58, 0x08, 0x29, 0xa0, 0x60, 0x0c, 0xf2, 0x00, 0x27, 0x02, 0x05, 0x20, 0x06, 0x4d, 0x02, 0x0b, + 0xc0, 0x02, 0x08, 0x3c, 0x80, 0x09, 0xa0, 0x39, 0x0a, 0xd4, 0x41, 0x8f, 0x50, 0x05, 0x09, 0xa4, + 0x5b, 0x4d, 0x00, 0xd8, 0x23, 0xc4, 0x96, 0x20, 0xc7, 0xac, 0x40, 0x2d, 0x53, 0x00, 0x64, 0x6b, + 0x20, 0x1d, 0x4a, 0x08, 0x32, 0x2a, 0x90, 0x0d, 0x46, 0x0e, 0x02, 0x0c, 0x79, 0x51, 0x08, 0x61, + 0xf0, 0x20, 0x63, 0xc5, 0x4b, 0x83, 0x1e, 0xfe, 0x57, 0xd3, 0x51, 0x40, 0xbe, 0xc0, 0x08, 0x42, + 0x00, 0x53, 0x30, 0xe8, 0x3f, 0x50, 0x14, 0x73, 0x80, 0x0b, 0xeb, 0x07, 0x61, 0x40, 0x00, 0x7d, + 0x5f, 0xf8, 0x38, 0x32, 0x7a, 0x03, 0xf7, 0x55, 0xa6, 0x78, 0x19, 0x54, 0x0c, 0xa8, 0x32, 0xa0, + 0x19, 0xa0, 0x65, 0xc4, 0x0b, 0xe2, 0x00, 0x98, 0x40, 0x33, 0xc1, 0x92, 0xfa, 0x10, 0x67, 0x80, + 0x08, +}; +const uint8_t* const _I_RFIDDolphinSend_97x61[] = {_I_RFIDDolphinSend_97x61_0}; + +const uint8_t _I_RFIDDolphinSuccess_108x57_0[] = { + 0x01, 0x00, 0xe7, 0x01, 0x00, 0x0f, 0x03, 0xff, 0x1f, 0x06, 0xd4, 0xe2, 0x01, 0xe0, 0x06, 0xd4, + 0x18, 0x04, 0x30, 0x30, 0x64, 0x60, 0x20, 0x20, 0x31, 0x86, 0x03, 0x62, 0x80, 0x03, 0x28, 0x80, + 0x36, 0x24, 0x00, 0x36, 0x00, 0x28, 0x5c, 0xc3, 0xe6, 0x00, 0x58, 0x40, 0xec, 0xc1, 0xb1, 0x04, + 0x02, 0x19, 0x24, 0x80, 0x0b, 0x02, 0x02, 0x40, 0x37, 0xc4, 0x8c, 0x2e, 0x40, 0x6f, 0x93, 0x8b, + 0x81, 0x07, 0x06, 0xdc, 0xc2, 0x38, 0x66, 0x50, 0x6a, 0xe2, 0x27, 0xe0, 0xd2, 0xfc, 0x08, 0x09, + 0x0c, 0x9c, 0x4b, 0x98, 0x34, 0xa0, 0xe1, 0xd5, 0x06, 0x8f, 0x92, 0xc2, 0x05, 0x1e, 0x42, 0xe1, + 0x81, 0xa3, 0xe2, 0xf0, 0xbc, 0x4c, 0x1a, 0xff, 0x2f, 0x9b, 0x80, 0xd8, 0xca, 0x05, 0x1f, 0x97, + 0xfd, 0xf8, 0x60, 0xd2, 0x01, 0x1e, 0x00, 0x1a, 0x5c, 0x00, 0x08, 0xc9, 0xc1, 0xab, 0x40, 0xf9, + 0x83, 0x46, 0x61, 0x00, 0xd8, 0x4a, 0x81, 0xab, 0xa0, 0xf3, 0x5f, 0xc6, 0x05, 0x58, 0x8a, 0xa4, + 0x09, 0x76, 0x21, 0xb1, 0xf2, 0x83, 0x4f, 0x5d, 0x1a, 0x01, 0x8c, 0x90, 0x1a, 0x31, 0x0d, 0x07, + 0xa9, 0x16, 0x50, 0x0a, 0xac, 0x34, 0xba, 0x42, 0xa1, 0x88, 0x50, 0x23, 0xaa, 0x72, 0xe0, 0x6a, + 0xa1, 0x4a, 0x32, 0x39, 0x88, 0x6c, 0x60, 0xc7, 0x82, 0xb0, 0x55, 0x60, 0xa2, 0x92, 0x80, 0xc0, + 0x43, 0x63, 0x03, 0x25, 0x96, 0xe3, 0x54, 0x33, 0x18, 0xc4, 0x90, 0x22, 0x21, 0x81, 0x81, 0x03, + 0x4a, 0xa9, 0x55, 0x7a, 0x17, 0xf3, 0x82, 0x9f, 0x6d, 0x5e, 0xa9, 0xb6, 0x50, 0x38, 0x70, 0x35, + 0x70, 0x15, 0x5a, 0xa9, 0xb8, 0xa3, 0x46, 0x12, 0x06, 0x9f, 0x83, 0x54, 0x8a, 0x28, 0x80, 0x34, + 0xfc, 0x08, 0x93, 0xaa, 0xc7, 0x40, 0x83, 0x83, 0x81, 0xd3, 0xa1, 0xd1, 0x08, 0x84, 0x0c, 0x24, + 0x3f, 0xed, 0x54, 0x18, 0x26, 0x50, 0x20, 0xd9, 0x42, 0x21, 0x90, 0x4c, 0x07, 0xff, 0xae, 0x52, + 0x20, 0x6a, 0xc4, 0x23, 0x1f, 0x88, 0x3f, 0xf0, 0x1a, 0x45, 0x31, 0xe7, 0x03, 0x4a, 0x41, 0xe0, + 0x69, 0x0f, 0xc2, 0x1e, 0x0d, 0x19, 0x80, 0x48, 0xa2, 0x10, 0xc5, 0x68, 0xdf, 0x0a, 0x82, 0xb9, + 0x28, 0x22, 0x2c, 0xe3, 0x0a, 0xd1, 0x2b, 0x0f, 0x00, 0x3c, 0x22, 0x91, 0x53, 0x9c, 0x50, 0x1a, + 0x30, 0x08, 0x39, 0x1c, 0x60, 0x6d, 0x12, 0x3d, 0x8c, 0xc2, 0x51, 0x00, 0x17, 0x0c, 0xe2, 0x01, + 0xff, 0x83, 0x84, 0xc6, 0x40, 0xb0, 0x19, 0x84, 0xd0, 0x1a, 0x5c, 0x08, 0x1f, 0xf8, 0x8c, 0x50, + 0x43, 0x08, 0xce, 0x2d, 0x06, 0x71, 0x5f, 0x17, 0xfe, 0x12, 0xdf, 0x20, 0x69, 0x55, 0x01, 0xa6, + 0x00, 0x18, 0x40, 0xa4, 0x80, 0x63, 0x3c, 0xb5, 0x03, 0x56, 0x08, 0x8b, 0x20, 0x10, 0xcf, 0x03, + 0x62, 0x08, 0x20, 0x00, 0x94, 0xc6, 0x01, 0x70, 0x01, 0x0c, 0xe8, 0x36, 0x20, 0xd3, 0xe0, 0x00, + 0xcb, 0x10, 0x02, 0x19, 0xf3, 0x9c, 0x41, 0xa3, 0x15, 0x31, 0x90, 0x00, 0x70, 0xc0, 0x21, 0xdd, + 0x86, 0xc4, 0x78, 0x3e, 0xa3, 0x71, 0xe0, 0x30, 0x20, 0x31, 0xbe, 0x86, 0xc4, 0x1a, 0x35, 0x40, + 0x20, 0x8d, 0x89, 0x28, 0x5b, 0xa0, 0xd9, 0xea, 0x3d, 0x44, 0x42, 0x87, 0x83, 0x48, 0x36, 0x49, + 0xe1, 0xa0, 0x75, 0x67, 0x8d, 0x41, 0x54, 0x14, 0x03, 0xf5, 0x2a, 0x06, 0x96, 0x03, 0x54, 0xc4, + 0x14, 0xd0, 0x83, 0x4a, 0xfb, 0x35, 0x06, 0x90, 0x38, 0x4e, 0x46, 0xb4, 0x10, 0xd9, 0x81, 0x49, + 0x72, 0x40, 0x01, 0x0a, 0x95, 0xd4, 0x36, 0x20, 0xd7, 0x55, 0x10, +}; +const uint8_t* const _I_RFIDDolphinSuccess_108x57[] = {_I_RFIDDolphinSuccess_108x57_0}; + +const uint8_t _I_RFIDSmallChip_14x14_0[] = { + 0x00, 0xfe, 0x0d, 0xb7, 0x1d, 0x07, 0x1c, 0xf9, 0x13, 0xcb, 0x1b, 0xcb, 0x1b, 0xf8, + 0x13, 0xfb, 0x1b, 0xfb, 0x1b, 0xf9, 0x03, 0x07, 0x1c, 0xb7, 0x1d, 0xf6, 0x0f, +}; +const uint8_t* const _I_RFIDSmallChip_14x14[] = {_I_RFIDSmallChip_14x14_0}; + +const uint8_t _I_SDQuestion_35x43_0[] = { + 0x01, 0x00, 0x67, 0x00, 0xf8, 0x7f, 0xc0, 0x03, 0x03, 0xfc, 0x01, 0x0a, 0x0f, 0x38, 0xa4, 0xe4, + 0xa4, 0x80, 0x4f, 0x0c, 0x20, 0x13, 0xc0, 0x9f, 0x80, 0x02, 0x15, 0xfe, 0x00, 0x04, 0x29, 0xfc, + 0x03, 0xfd, 0x07, 0xfa, 0x47, 0xe7, 0xdf, 0xc8, 0x3f, 0xea, 0x1f, 0x7f, 0xfc, 0x41, 0xff, 0xb8, + 0xff, 0xf8, 0x10, 0x7f, 0xe0, 0x4e, 0xef, 0x86, 0x08, 0x68, 0x33, 0xf1, 0x10, 0xff, 0x3f, 0xf1, + 0xf1, 0x60, 0x81, 0x06, 0x1e, 0x36, 0x10, 0x20, 0xe1, 0xc0, 0x87, 0xc7, 0x02, 0x0f, 0xd3, 0xff, + 0xe3, 0x02, 0x0f, 0xe8, 0x08, 0x7f, 0xd0, 0x21, 0x89, 0xc4, 0x08, 0x9f, 0x70, 0x21, 0x9a, 0x08, + 0x08, 0xc1, 0x89, 0x02, 0x20, 0x62, 0x40, 0x8f, 0xfe, 0x68, 0x98, +}; +const uint8_t* const _I_SDQuestion_35x43[] = {_I_SDQuestion_35x43_0}; + +const uint8_t _I_Cry_dolph_55x52_0[] = { + 0x01, 0x00, 0xe8, 0x00, 0x00, 0x0f, 0xe3, 0xff, 0x01, 0x03, 0x1f, 0xfb, 0xff, 0x0f, 0x02, 0x96, + 0x02, 0x0f, 0x00, 0x9f, 0x01, 0x8b, 0xc0, 0x12, 0x1f, 0x80, 0x18, 0xae, 0x00, 0x21, 0xe0, 0x07, + 0x0a, 0x30, 0x0a, 0x28, 0x18, 0x08, 0x61, 0x80, 0x62, 0x83, 0x00, 0x90, 0x14, 0x61, 0x02, 0x0c, + 0x16, 0x00, 0x76, 0x60, 0x66, 0x98, 0x0b, 0x04, 0x90, 0x60, 0x66, 0xb0, 0x00, 0x48, 0x0d, 0x21, + 0x21, 0x03, 0x30, 0x74, 0x40, 0xd3, 0x80, 0x03, 0x34, 0x04, 0xc0, 0x52, 0x00, 0x32, 0xc7, 0xa0, + 0x18, 0x80, 0x31, 0x80, 0x07, 0xe1, 0x01, 0x37, 0x18, 0x50, 0x80, 0xc2, 0x92, 0x10, 0x31, 0xe8, + 0x23, 0xe9, 0x63, 0x86, 0x54, 0x3f, 0xe0, 0xe1, 0x0d, 0x96, 0x83, 0xfc, 0x06, 0x40, 0x69, 0x6c, + 0x3c, 0x60, 0xd2, 0xfc, 0xc0, 0x60, 0x58, 0x48, 0x0c, 0x1b, 0x81, 0x08, 0x14, 0x9c, 0x1a, 0x81, + 0x04, 0x03, 0x46, 0x80, 0x0c, 0x50, 0x26, 0x21, 0xc1, 0x94, 0x26, 0x14, 0x27, 0x8a, 0x40, 0xc0, + 0xc2, 0xe7, 0x26, 0x40, 0x81, 0x86, 0xc0, 0x6b, 0x28, 0x64, 0x0f, 0x01, 0x10, 0x4e, 0x14, 0x60, + 0x0c, 0x29, 0x02, 0x48, 0x8b, 0x5c, 0x45, 0x22, 0x01, 0x10, 0x31, 0x3a, 0x4c, 0x0c, 0x34, 0x06, + 0xf1, 0xd8, 0x00, 0xc5, 0x1a, 0x64, 0x94, 0x0c, 0xc0, 0x37, 0x52, 0x20, 0x81, 0x84, 0x26, 0x3e, + 0x88, 0x0c, 0x38, 0x28, 0x54, 0x0e, 0xac, 0x1f, 0xe1, 0x3f, 0x06, 0x96, 0x82, 0x7e, 0x29, 0x4a, + 0xaf, 0xfd, 0x76, 0x30, 0x3a, 0x41, 0x14, 0x7f, 0xd0, 0xf8, 0x78, 0x18, 0xaa, 0x9f, 0xd4, 0xe0, + 0x83, 0x4f, 0xf5, 0xf7, 0x38, 0x0b, 0x9c, 0x6a, 0x1f, 0x5b, 0x5c, 0x00, +}; +const uint8_t* const _I_Cry_dolph_55x52[] = {_I_Cry_dolph_55x52_0}; + +const uint8_t _I_Alert_9x8_0[] = { + 0x00, + 0x10, + 0x00, + 0x38, + 0x00, + 0x28, + 0x00, + 0x6c, + 0x00, + 0x6c, + 0x00, + 0xfe, + 0x00, + 0xee, + 0x00, + 0xff, + 0x01, +}; +const uint8_t* const _I_Alert_9x8[] = {_I_Alert_9x8_0}; + +const uint8_t _I_Attention_5x8_0[] = { + 0x00, + 0x0e, + 0x0a, + 0x0a, + 0x0a, + 0x0e, + 0x04, + 0x00, + 0x0e, +}; +const uint8_t* const _I_Attention_5x8[] = {_I_Attention_5x8_0}; + +const uint8_t _I_Background_128x11_0[] = { + 0x01, 0x00, 0x70, 0x00, 0xff, 0x40, 0x40, 0xc9, 0xe0, 0xff, 0x80, 0x06, 0x1e, 0x08, 0x38, + 0x0c, 0x0c, 0x1e, 0x93, 0x00, 0x19, 0x46, 0x01, 0x07, 0x7d, 0x83, 0x03, 0xd2, 0x31, 0xff, + 0xdb, 0xd5, 0x66, 0x20, 0x83, 0xc0, 0xff, 0x05, 0x24, 0x00, 0x1c, 0x78, 0x28, 0xbc, 0x40, + 0x72, 0xbf, 0xcf, 0x47, 0xeb, 0x40, 0xdb, 0x7a, 0xbf, 0xf0, 0x40, 0x39, 0x60, 0x28, 0x3f, + 0xe0, 0xa0, 0xea, 0x80, 0x63, 0x3f, 0x0b, 0x17, 0xe4, 0x3e, 0x5a, 0xbc, 0xf9, 0x99, 0x70, + 0x1f, 0x81, 0x50, 0xc0, 0x80, 0xe7, 0x3e, 0x1e, 0x9d, 0x57, 0xfb, 0x7f, 0x23, 0x15, 0xb0, + 0x12, 0x5b, 0x5b, 0x02, 0x1d, 0x8c, 0xc3, 0x80, 0x24, 0x9e, 0x03, 0x80, 0x5e, 0x40, 0x00, + 0xa1, 0x88, 0x0e, 0x98, 0x00, 0x7b, 0x07, 0x08, 0xb2, 0x44, 0x41, +}; +const uint8_t* const _I_Background_128x11[] = {_I_Background_128x11_0}; + +const uint8_t _I_Battery_26x8_0[] = { + 0x01, 0x00, 0x13, 0x00, 0xff, 0x7f, 0xef, 0xf0, 0x08, 0x0c, 0x03, 0x00, + 0x03, 0x38, 0x18, 0x0c, 0xa0, 0x40, 0x36, 0x05, 0x98, 0x6d, 0x00, +}; +const uint8_t* const _I_Battery_26x8[] = {_I_Battery_26x8_0}; + +const uint8_t _I_Bluetooth_Connected_16x8_0[] = { + 0x00, + 0x04, + 0x00, + 0x0d, + 0x00, + 0x16, + 0x60, + 0x4c, + 0x97, + 0x4c, + 0x97, + 0x16, + 0x60, + 0x0d, + 0x00, + 0x04, + 0x00, +}; +const uint8_t* const _I_Bluetooth_Connected_16x8[] = {_I_Bluetooth_Connected_16x8_0}; + +const uint8_t _I_Bluetooth_Idle_5x8_0[] = { + 0x00, + 0x04, + 0x0d, + 0x16, + 0x0c, + 0x0c, + 0x16, + 0x0d, + 0x04, +}; +const uint8_t* const _I_Bluetooth_Idle_5x8[] = {_I_Bluetooth_Idle_5x8_0}; + +const uint8_t _I_Charging_lightning_9x10_0[] = { + 0x00, 0x40, 0x01, 0xa0, 0x00, 0x50, 0x00, 0xe8, 0x01, 0x84, 0x00, + 0x42, 0x00, 0x2f, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x05, 0x00, +}; +const uint8_t* const _I_Charging_lightning_9x10[] = {_I_Charging_lightning_9x10_0}; + +const uint8_t _I_Charging_lightning_mask_9x10_0[] = { + 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x78, 0x00, + 0x3c, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, +}; +const uint8_t* const _I_Charging_lightning_mask_9x10[] = {_I_Charging_lightning_mask_9x10_0}; + +const uint8_t _I_GameMode_11x8_0[] = { + 0x00, + 0x20, + 0x00, + 0xfe, + 0x03, + 0xfb, + 0x07, + 0x71, + 0x05, + 0xfb, + 0x07, + 0x8f, + 0x07, + 0x07, + 0x07, + 0x03, + 0x06, +}; +const uint8_t* const _I_GameMode_11x8[] = {_I_GameMode_11x8_0}; + +const uint8_t _I_Hidden_window_9x8_0[] = { + 0x00, + 0xfe, + 0x01, + 0x7e, + 0x01, + 0xfe, + 0x01, + 0x02, + 0x01, + 0x7f, + 0x01, + 0x41, + 0x01, + 0xc1, + 0x01, + 0x7f, + 0x00, +}; +const uint8_t* const _I_Hidden_window_9x8[] = {_I_Hidden_window_9x8_0}; + +const uint8_t _I_Lock_8x8_0[] = { + 0x00, + 0x3c, + 0x42, + 0x42, + 0xff, + 0xff, + 0xe7, + 0xff, + 0xff, +}; +const uint8_t* const _I_Lock_8x8[] = {_I_Lock_8x8_0}; + +const uint8_t _I_SDcardFail_11x8_0[] = { + 0x00, + 0xff, + 0x07, + 0xb7, + 0x07, + 0xff, + 0x07, + 0x87, + 0x07, + 0x7b, + 0x07, + 0xff, + 0x07, + 0xff, + 0x07, + 0x67, + 0x00, +}; +const uint8_t* const _I_SDcardFail_11x8[] = {_I_SDcardFail_11x8_0}; + +const uint8_t _I_SDcardMounted_11x8_0[] = { + 0x01, + 0x00, + 0x09, + 0x00, + 0xff, + 0xc1, + 0xff, + 0xf0, + 0x40, + 0x1c, + 0xd9, + 0xe0, + 0x00, +}; +const uint8_t* const _I_SDcardMounted_11x8[] = {_I_SDcardMounted_11x8_0}; + +const uint8_t _I_Dynamic_9x7_0[] = { + 0x00, + 0x00, + 0x00, + 0x0c, + 0x01, + 0x92, + 0x00, + 0x92, + 0x00, + 0x61, + 0x00, + 0x00, + 0x00, + 0xff, + 0x01, +}; +const uint8_t* const _I_Dynamic_9x7[] = {_I_Dynamic_9x7_0}; + +const uint8_t _I_Lock_7x8_0[] = { + 0x00, + 0x1c, + 0x22, + 0x22, + 0x7f, + 0x7f, + 0x77, + 0x7f, + 0x3e, +}; +const uint8_t* const _I_Lock_7x8[] = {_I_Lock_7x8_0}; + +const uint8_t _I_MHz_25x11_0[] = { + 0x01, 0x00, 0x21, 0x00, 0xe1, 0xe1, 0xa0, 0x30, 0x0f, 0x38, 0x0c, 0xbf, 0xe0, + 0x34, 0xfe, 0xc0, 0x7b, 0x7f, 0xe0, 0x19, 0xf0, 0x60, 0x1d, 0xbc, 0x35, 0x84, + 0x36, 0x53, 0x10, 0x19, 0x46, 0x40, 0x64, 0x13, 0x10, 0x19, 0x80, +}; +const uint8_t* const _I_MHz_25x11[] = {_I_MHz_25x11_0}; + +const uint8_t _I_Quest_7x8_0[] = { + 0x00, + 0x1e, + 0x33, + 0x33, + 0x30, + 0x18, + 0x0c, + 0x00, + 0x0c, +}; +const uint8_t* const _I_Quest_7x8[] = {_I_Quest_7x8_0}; + +const uint8_t _I_Raw_9x7_0[] = { + 0x00, + 0x08, + 0x00, + 0x4c, + 0x00, + 0xdc, + 0x00, + 0xfe, + 0x00, + 0xff, + 0x01, + 0x00, + 0x00, + 0xff, + 0x01, +}; +const uint8_t* const _I_Raw_9x7[] = {_I_Raw_9x7_0}; + +const uint8_t _I_Scanning_123x52_0[] = { + 0x01, 0x00, 0xd9, 0x01, 0x00, 0x78, 0x03, 0xc0, 0x1f, 0x00, 0xe0, 0x7f, 0xc1, 0xfb, 0xf0, 0x80, + 0x41, 0xc0, 0xc7, 0x03, 0x07, 0xbe, 0xb2, 0x07, 0x18, 0x07, 0xe5, 0x5a, 0x0b, 0x60, 0x02, 0x96, + 0x02, 0x04, 0x0f, 0x7c, 0x0c, 0x2b, 0x00, 0x0f, 0xb0, 0x60, 0xc5, 0x60, 0x10, 0x10, 0x30, 0xd0, + 0x01, 0xef, 0x40, 0x80, 0x4e, 0x20, 0x00, 0xb2, 0x07, 0xac, 0x40, 0x04, 0x32, 0x08, 0x05, 0xd2, + 0x01, 0x04, 0x07, 0x96, 0x08, 0x48, 0x50, 0x08, 0x40, 0x3c, 0xed, 0x00, 0xf3, 0x90, 0x60, 0xa0, + 0x50, 0x60, 0x28, 0x82, 0x08, 0x44, 0x3d, 0x0f, 0x04, 0x3c, 0x1e, 0x90, 0xa8, 0x60, 0xe0, 0xc1, + 0xe3, 0xa0, 0x8e, 0x0d, 0x8c, 0x1e, 0x51, 0x48, 0x06, 0x08, 0x64, 0x5e, 0x0e, 0x9c, 0x0c, 0x1e, + 0x51, 0x00, 0x7a, 0x50, 0x41, 0xe5, 0x90, 0xd3, 0xd0, 0x24, 0x80, 0xf7, 0x90, 0x83, 0xcb, 0x91, + 0x7f, 0xc2, 0x4a, 0x01, 0xe5, 0x84, 0x07, 0x8f, 0xff, 0x01, 0x20, 0x30, 0x0e, 0x22, 0xcf, 0x3e, + 0xc4, 0x03, 0xd3, 0xf5, 0xff, 0x07, 0x8c, 0x40, 0xc0, 0x34, 0x4b, 0x24, 0xb8, 0x1e, 0x9f, 0xcd, + 0x7f, 0xfe, 0x2a, 0x0f, 0x13, 0x00, 0xd1, 0x3c, 0x92, 0xfc, 0x44, 0xa8, 0x1e, 0x31, 0x8e, 0xa7, + 0x7e, 0x3f, 0x14, 0x07, 0xb5, 0x43, 0xff, 0x00, 0xff, 0x47, 0xfd, 0x93, 0xf2, 0x78, 0x83, 0xd3, + 0x89, 0x2f, 0xf5, 0x34, 0x06, 0x03, 0x1a, 0xfe, 0x17, 0xc8, 0x1e, 0x78, 0x4c, 0x44, 0xbe, 0x3a, + 0x87, 0x1b, 0xec, 0x78, 0xbe, 0x51, 0x82, 0x79, 0xe1, 0xa3, 0xfc, 0x65, 0xc0, 0xe1, 0x2f, 0xb1, + 0x60, 0x79, 0x3e, 0xc4, 0xde, 0x54, 0x6f, 0xaf, 0x0d, 0xb2, 0x34, 0x48, 0x0c, 0x88, 0x1e, 0x2b, + 0xe1, 0x47, 0x9e, 0x1e, 0x15, 0x7c, 0x02, 0x0d, 0x12, 0x07, 0x22, 0xc4, 0x32, 0xc9, 0x5e, 0x74, + 0x58, 0x28, 0x8c, 0x6f, 0xda, 0x48, 0x1d, 0x08, 0x1e, 0x8c, 0xf2, 0xc3, 0x0f, 0x47, 0x81, 0x81, + 0xd2, 0xc1, 0xe8, 0x50, 0x88, 0x18, 0x3c, 0x82, 0x65, 0x05, 0x01, 0x0c, 0xc2, 0x01, 0xd2, 0xff, + 0xe0, 0x44, 0xa2, 0x00, 0x22, 0xf0, 0xc0, 0x03, 0xc3, 0x00, 0x24, 0x0f, 0x4a, 0x3f, 0x80, 0x07, + 0xa0, 0x10, 0xa0, 0xb8, 0x08, 0x00, 0xa4, 0x7a, 0x31, 0xf8, 0x04, 0x24, 0x1e, 0x71, 0x00, 0xf9, + 0xf8, 0x4e, 0xe3, 0xe9, 0x87, 0xc0, 0x22, 0x40, 0xf3, 0x00, 0x10, 0xfc, 0x63, 0xc5, 0x01, 0xf4, + 0x03, 0xc6, 0x28, 0x0f, 0x61, 0xf9, 0x40, 0x2e, 0x1e, 0xe0, 0x78, 0xc6, 0x01, 0xed, 0x07, 0x01, + 0x94, 0xf3, 0xf2, 0x0f, 0x10, 0x03, 0x9d, 0x8b, 0x55, 0x9f, 0xf8, 0x03, 0xe6, 0x03, 0x40, 0xaa, + 0xff, 0x6b, 0xff, 0x08, 0xb8, 0x7e, 0x70, 0x1e, 0xaf, 0xfc, 0xff, 0xc0, 0x0f, 0x90, 0xf8, 0xf5, + 0x7f, 0xf1, 0xf0, 0x7d, 0x40, 0xb0, 0x1a, 0xaf, 0xfc, 0x3f, 0xc1, 0x13, 0x0f, 0xcc, 0x02, 0xab, + 0x55, 0x81, 0xfc, 0x22, 0xa1, 0xf9, 0x2f, 0x0e, 0xaa, 0x01, 0xb0, 0x85, 0x07, 0xea, 0xa9, 0x75, + 0x80, 0x56, 0x21, 0xa1, 0xfa, 0xd4, 0x1f, 0x07, 0x50, 0xa5, 0x35, 0xfb, 0xdf, 0x00, 0x83, 0x5c, + 0x4a, 0x90, 0x7e, 0x00, 0x05, 0xae, 0x1a, 0x88, 0x7e, 0x40, 0x05, 0xd7, 0x07, 0x44, 0x00, 0xb2, + 0x78, 0x0b, 0xd8, 0x3e, 0x4f, 0x27, 0xc0, 0x8f, 0x83, 0xe6, 0x54, 0x03, 0x2f, 0x83, 0xd0, 0xc0, + 0x0d, 0x55, 0x01, 0x97, 0xf0, 0x9d, 0x29, 0xd1, 0x01, 0x90, 0xba, 0xa0, 0x00, +}; +const uint8_t* const _I_Scanning_123x52[] = {_I_Scanning_123x52_0}; + +const uint8_t _I_Static_9x7_0[] = { + 0x01, + 0x00, + 0x0c, + 0x00, + 0xbe, + 0x40, + 0x28, + 0x80, + 0x14, + 0xe3, + 0xc0, + 0x41, + 0x63, + 0xff, + 0x80, + 0x80, +}; +const uint8_t* const _I_Static_9x7[] = {_I_Static_9x7_0}; + +const uint8_t _I_Unlock_7x8_0[] = { + 0x00, + 0x1c, + 0x22, + 0x02, + 0x4f, + 0x67, + 0x73, + 0x79, + 0x3c, +}; +const uint8_t* const _I_Unlock_7x8[] = {_I_Unlock_7x8_0}; + +const uint8_t _I_Auth_62x31_0[] = { + 0x01, 0x00, 0xaf, 0x00, 0x00, 0x47, 0xc2, 0xfe, 0x07, 0x58, 0x66, 0x02, 0x02, 0x07, 0x48, + 0x1c, 0x02, 0x0c, 0x06, 0x3c, 0x00, 0x08, 0x61, 0x00, 0x73, 0xa0, 0x00, 0x86, 0x20, 0x02, + 0x1b, 0x04, 0x02, 0x40, 0x04, 0x10, 0x11, 0x01, 0xc4, 0x18, 0x40, 0x72, 0xf0, 0x40, 0x40, + 0xe4, 0x1a, 0x20, 0x38, 0xc2, 0x3e, 0x00, 0x71, 0xbc, 0x05, 0xca, 0x11, 0x08, 0x80, 0xe0, + 0x30, 0xc0, 0x72, 0x82, 0x7d, 0x20, 0x44, 0x81, 0x80, 0x81, 0xcb, 0x75, 0x05, 0x02, 0x08, + 0x1c, 0xe7, 0x50, 0x58, 0xc0, 0x94, 0x40, 0xe5, 0xfa, 0x82, 0xc1, 0xbf, 0x06, 0xc1, 0x80, + 0x40, 0x80, 0xe3, 0x00, 0xbe, 0x40, 0x3f, 0x10, 0x18, 0x17, 0xd0, 0xd0, 0x33, 0xf3, 0xa0, + 0xc0, 0xe0, 0x52, 0x88, 0x26, 0x02, 0x3e, 0x1d, 0x18, 0x14, 0x08, 0xa0, 0x3c, 0x08, 0x78, + 0x3c, 0xc0, 0xe3, 0xe0, 0x83, 0x87, 0xcd, 0x32, 0x42, 0x11, 0x17, 0x90, 0x04, 0x61, 0x9f, + 0xf8, 0x06, 0x20, 0x0e, 0x41, 0xb1, 0x9e, 0x1b, 0x44, 0x2e, 0x5f, 0x0f, 0xfc, 0x0c, 0x0e, + 0x80, 0x02, 0x80, 0xc1, 0x00, 0xe8, 0xab, 0x11, 0xf9, 0x01, 0xca, 0xe0, 0x07, 0x68, 0x60, + 0xb4, 0x40, 0xe7, 0xfe, 0x1f, 0x88, 0x1d, 0x09, 0x82, 0x28, 0x10, 0xba, 0x01, 0xcc, +}; +const uint8_t* const _I_Auth_62x31[] = {_I_Auth_62x31_0}; + +const uint8_t _I_Connect_me_62x31_0[] = { + 0x01, 0x00, 0xb7, 0x00, 0x00, 0x47, 0xc2, 0xfe, 0x07, 0x58, 0x66, 0x02, 0x02, 0x07, 0x48, 0x1c, + 0x02, 0x0c, 0x06, 0x3c, 0x00, 0x08, 0x61, 0x00, 0x73, 0xa0, 0x00, 0x86, 0x20, 0x02, 0x1b, 0xe4, + 0x02, 0x40, 0x04, 0x10, 0x11, 0x51, 0x01, 0x86, 0x07, 0x2b, 0x60, 0x1c, 0xc3, 0x44, 0x0f, 0x18, + 0x47, 0xc0, 0x0e, 0x37, 0x80, 0xf9, 0x42, 0x21, 0x10, 0x1c, 0x06, 0x1a, 0x01, 0x82, 0x80, 0x41, + 0x3e, 0x90, 0x22, 0x40, 0xc0, 0x40, 0xe5, 0xba, 0x82, 0xd8, 0x20, 0x00, 0x73, 0x9d, 0x41, 0x63, + 0x1e, 0x00, 0x39, 0xfe, 0xa0, 0xb0, 0x6f, 0xc0, 0x7c, 0xa0, 0x40, 0x71, 0x16, 0x90, 0x1c, 0xbe, + 0x86, 0x81, 0x9f, 0x81, 0xce, 0x51, 0x04, 0xc0, 0x47, 0xe0, 0x1f, 0xe0, 0x38, 0x94, 0x07, 0x81, + 0x0f, 0x80, 0x4a, 0x00, 0xe7, 0xe0, 0x83, 0x81, 0xcd, 0x32, 0x42, 0x11, 0x03, 0x9c, 0x0a, 0x19, + 0xff, 0x80, 0x4e, 0x00, 0xe5, 0x0c, 0x81, 0xcf, 0x20, 0x21, 0xc1, 0x03, 0x8f, 0xc3, 0xff, 0x03, + 0x80, 0x92, 0x44, 0x3e, 0x40, 0x01, 0x80, 0xc1, 0x20, 0x60, 0x73, 0x55, 0x8c, 0x0a, 0x08, 0x07, + 0x3b, 0x80, 0x1c, 0x61, 0x00, 0x73, 0x86, 0x11, 0x48, 0x0e, 0x5f, 0xe1, 0xf8, 0xc3, 0x00, 0xe7, + 0xf8, 0x6c, 0x0c, 0x42, 0x40, 0x17, 0x30, 0x38, 0xcc, 0x24, 0x00, +}; +const uint8_t* const _I_Connect_me_62x31[] = {_I_Connect_me_62x31_0}; + +const uint8_t _I_Connected_62x31_0[] = { + 0x01, 0x00, 0xaa, 0x00, 0x00, 0x47, 0xc2, 0xfe, 0x07, 0x58, 0x66, 0x02, 0x02, 0x07, 0x48, 0x1c, + 0x02, 0x0c, 0x06, 0x3c, 0x00, 0x08, 0x61, 0x00, 0x73, 0xa0, 0x00, 0x86, 0x20, 0x07, 0x39, 0x00, + 0x09, 0x01, 0x88, 0x07, 0x70, 0xd1, 0x09, 0x0b, 0xe0, 0x07, 0x1b, 0xc0, 0x1c, 0xe1, 0x10, 0x1c, + 0x06, 0x18, 0x0e, 0x50, 0x4f, 0xa4, 0x08, 0x90, 0x24, 0x92, 0x82, 0x6e, 0xa0, 0xb6, 0x08, 0x07, + 0x04, 0x10, 0x30, 0x49, 0xd4, 0x16, 0x31, 0xe0, 0xa0, 0xfc, 0x80, 0xe3, 0xfa, 0x82, 0xc1, 0xbf, + 0x14, 0x08, 0x64, 0x06, 0x04, 0x07, 0x18, 0x05, 0xf2, 0x81, 0x04, 0x81, 0x40, 0xbe, 0x86, 0x81, + 0x9f, 0xe0, 0x20, 0x80, 0x81, 0x94, 0x41, 0x30, 0x11, 0xf0, 0x39, 0x94, 0x07, 0x81, 0x0f, 0x03, + 0xaf, 0x82, 0x0f, 0x00, 0x84, 0x81, 0xc5, 0x32, 0x42, 0x11, 0x98, 0x89, 0xc6, 0x01, 0x02, 0x86, + 0x7f, 0xc9, 0x03, 0x03, 0x03, 0x8c, 0x32, 0x07, 0x3c, 0x2c, 0x08, 0x3c, 0xbe, 0x1f, 0xf8, 0x18, + 0x1d, 0x00, 0x05, 0x81, 0x0e, 0x08, 0x1c, 0xf0, 0x0a, 0xc1, 0x03, 0xa5, 0xc0, 0x0e, 0xd0, 0xc4, + 0xc8, 0x81, 0xcf, 0xfd, 0x03, 0x03, 0xaf, 0xe2, 0x02, 0xb1, 0x10, 0xba, 0x01, 0xcc, +}; +const uint8_t* const _I_Connected_62x31[] = {_I_Connected_62x31_0}; + +const uint8_t _I_Drive_112x35_0[] = { + 0x01, 0x00, 0x72, 0x00, 0xf0, 0x7f, 0xc0, 0x0f, 0x1f, 0x06, 0x94, 0x40, 0x2f, 0x12, 0x00, + 0x19, 0x42, 0x01, 0xb1, 0x40, 0x01, 0x94, 0x10, 0x1b, 0x18, 0x00, 0x19, 0x41, 0x81, 0xb9, + 0x07, 0x06, 0xc9, 0x24, 0x81, 0xb4, 0x02, 0x20, 0x6f, 0x83, 0x66, 0x7c, 0x0d, 0xc9, 0x10, + 0x6f, 0xc1, 0xbe, 0x0d, 0xf2, 0x9f, 0x83, 0x7c, 0x14, 0x3f, 0x8f, 0xff, 0xe4, 0x1b, 0x4c, + 0xe1, 0xc4, 0x83, 0x6a, 0x1f, 0x00, 0xc7, 0x8d, 0xfc, 0xc3, 0xe0, 0xdf, 0x06, 0xfa, 0xd7, + 0xc3, 0x7e, 0x99, 0xf0, 0x6d, 0x7f, 0xc0, 0x02, 0x0d, 0xcb, 0xf8, 0x37, 0x27, 0xe3, 0x7c, + 0x80, 0x2a, 0x84, 0x00, 0xf4, 0x00, 0x19, 0x47, 0xc1, 0xb1, 0x20, 0x01, 0x97, 0xf8, 0x92, + 0x40, 0x05, 0x0b, 0x9f, 0xf0, 0x1b, 0x03, 0x33, 0x7f, 0x08, 0x01, 0xc9, 0xe6, +}; +const uint8_t* const _I_Drive_112x35[] = {_I_Drive_112x35_0}; + +const uint8_t _I_Error_62x31_0[] = { + 0x01, 0x00, 0x9e, 0x00, 0x00, 0x47, 0xc2, 0xfe, 0x07, 0x58, 0x66, 0x02, 0x02, 0x07, 0x48, + 0x1c, 0x02, 0x0c, 0x06, 0x3c, 0x00, 0x08, 0x61, 0x00, 0x73, 0xa0, 0x00, 0x86, 0x20, 0x07, + 0x39, 0x00, 0x09, 0x01, 0x88, 0x07, 0x70, 0xd1, 0x09, 0x0b, 0xe0, 0x07, 0x38, 0x1c, 0x62, + 0x11, 0x08, 0x80, 0x8c, 0x8a, 0x0f, 0x1c, 0x82, 0x7d, 0x20, 0x58, 0x0b, 0xe4, 0x02, 0x1d, + 0x0e, 0x82, 0x6e, 0xa0, 0xb8, 0x0c, 0x1c, 0x02, 0x39, 0x07, 0x82, 0x4e, 0xa0, 0xb7, 0x08, + 0x04, 0x07, 0x71, 0x03, 0x82, 0x7e, 0xa0, 0xb0, 0xe8, 0x04, 0x0b, 0xe1, 0x01, 0x81, 0x01, + 0xc6, 0x01, 0xc0, 0x81, 0xf8, 0x01, 0x42, 0x27, 0x18, 0x04, 0xc0, 0x1e, 0x63, 0x71, 0x3d, + 0x0c, 0x08, 0x3e, 0x20, 0xa1, 0x22, 0x94, 0x08, 0x5e, 0x21, 0x51, 0x0f, 0x08, 0xbc, 0x47, + 0xe2, 0x07, 0x29, 0x81, 0x40, 0x49, 0xe2, 0x07, 0x28, 0x61, 0x80, 0x4b, 0xe2, 0x07, 0x28, + 0x19, 0xe0, 0xc0, 0xe2, 0x0d, 0x18, 0xc0, 0x1d, 0x00, 0x02, 0xa8, 0x30, 0x39, 0x2e, 0x10, + 0x0e, 0x5e, 0x00, 0x3b, 0x7e, 0x00, 0xec, 0x46, 0x10, 0x3f, 0x80, 0xc8, +}; +const uint8_t* const _I_Error_62x31[] = {_I_Error_62x31_0}; + +const uint8_t _I_Updating_32x40_0[] = { + 0x01, 0x00, 0x56, 0x00, 0xc0, 0x7f, 0xc0, 0x03, 0xc0, 0x01, 0x97, 0x82, 0x07, 0x00, 0xe0, + 0x5c, 0x00, 0x65, 0x38, 0x01, 0x94, 0x70, 0x06, 0x50, 0xe0, 0x19, 0x41, 0xc0, 0x65, 0xff, + 0x01, 0xb4, 0x0c, 0x02, 0x7e, 0x08, 0x38, 0x0c, 0x7c, 0xd6, 0x70, 0x18, 0xfb, 0xfe, 0xfc, + 0x0c, 0x18, 0xc8, 0x78, 0x20, 0x33, 0x81, 0x8f, 0x8a, 0x07, 0x3e, 0xbe, 0x70, 0x38, 0x71, + 0xff, 0xc7, 0x0f, 0xc7, 0x0f, 0xf8, 0x71, 0xc0, 0x76, 0x13, 0x30, 0xd9, 0x88, 0xcc, 0x5f, + 0x03, 0xb2, 0x21, 0xa1, 0x2c, 0xc0, 0x26, 0x82, 0x10, 0x1f, 0x80, 0xd1, 0x24, 0x40, 0x04, +}; +const uint8_t* const _I_Updating_32x40[] = {_I_Updating_32x40_0}; + +const uint8_t _I_DolphinMafia_115x62_0[] = { + 0x01, 0x00, 0x21, 0x02, 0x00, 0x1e, 0x02, 0x06, 0x0e, 0xcb, 0x04, 0x10, 0x1d, 0x91, 0x88, 0x40, + 0x3b, 0x20, 0xc0, 0xec, 0xc0, 0x40, 0x62, 0x03, 0xac, 0x80, 0x03, 0xb2, 0x31, 0x00, 0x90, 0x03, + 0xae, 0x5e, 0x0e, 0xcf, 0xc4, 0x56, 0x01, 0x40, 0x07, 0x56, 0xbe, 0x14, 0x0e, 0x2f, 0xf1, 0x5e, + 0x2a, 0xa1, 0xd1, 0xc0, 0x7c, 0x3f, 0xf0, 0x70, 0x73, 0x70, 0x35, 0x41, 0xd1, 0xc0, 0x7f, 0xff, + 0xf0, 0xf0, 0x73, 0x50, 0x03, 0xa4, 0x0d, 0x10, 0x74, 0x07, 0x46, 0x55, 0xe0, 0x07, 0x10, 0xb1, + 0xc3, 0xa3, 0x55, 0xfe, 0x03, 0x88, 0x94, 0xe1, 0xd1, 0xd5, 0x03, 0x4a, 0x3e, 0x59, 0x9e, 0xaf, + 0xfe, 0xff, 0x05, 0x60, 0x4e, 0xab, 0xf5, 0xff, 0x95, 0xb4, 0xa4, 0x3a, 0x3f, 0xd0, 0xe0, 0xfa, + 0x20, 0x20, 0xf8, 0xd5, 0xff, 0xb5, 0xf0, 0x0f, 0x88, 0x3a, 0x6a, 0xbf, 0xf8, 0xaf, 0x82, 0x6f, + 0x03, 0x07, 0x47, 0xaf, 0xff, 0x0a, 0xfe, 0x5f, 0xc1, 0xd3, 0xf6, 0xbf, 0xe0, 0x7f, 0xfe, 0xf0, + 0x73, 0x41, 0x00, 0x43, 0xfa, 0xd7, 0xf8, 0x27, 0xfe, 0xe0, 0x73, 0x40, 0x80, 0x43, 0xfe, 0xab, + 0xfe, 0x21, 0xfc, 0xe5, 0x9b, 0x05, 0x48, 0xea, 0x3f, 0xc8, 0xfa, 0xc4, 0x66, 0x07, 0x44, 0x0e, + 0x8f, 0x00, 0xb0, 0x2b, 0x31, 0x07, 0x0f, 0x00, 0x1c, 0x72, 0x00, 0x70, 0xf8, 0x37, 0xe5, 0x81, + 0xff, 0x89, 0x08, 0xf2, 0x71, 0x80, 0x20, 0xfe, 0x2b, 0xf0, 0x5f, 0xc0, 0x38, 0xc8, 0xa5, 0x60, + 0xc3, 0x00, 0xc7, 0xf9, 0xaf, 0x81, 0x2d, 0x04, 0x34, 0x40, 0xe1, 0x98, 0x47, 0x68, 0x04, 0x92, + 0xab, 0xc0, 0x7e, 0xb7, 0xf7, 0x39, 0x03, 0x85, 0x8e, 0x24, 0xf1, 0xc0, 0x7f, 0xf5, 0x78, 0x0f, + 0x53, 0xb4, 0xbc, 0x1f, 0xb8, 0x1a, 0x0c, 0x61, 0xc5, 0x82, 0xab, 0xc0, 0x3e, 0xa3, 0xa2, 0xfc, + 0x07, 0x46, 0x09, 0x60, 0x19, 0x8f, 0x80, 0xec, 0x38, 0x08, 0x52, 0x6c, 0xb8, 0xdc, 0x28, 0x7c, + 0x10, 0x2a, 0x5f, 0x0f, 0xfc, 0x5a, 0x01, 0x05, 0x1a, 0x8e, 0x02, 0x02, 0x1d, 0x1f, 0x81, 0xa8, + 0xbe, 0x13, 0xf8, 0x52, 0x2c, 0x8c, 0x62, 0x77, 0x42, 0x11, 0x40, 0xe0, 0xca, 0x93, 0x8e, 0x03, + 0x8a, 0x30, 0x10, 0x48, 0x54, 0x03, 0x04, 0xbb, 0x2c, 0x00, 0x0c, 0x64, 0x80, 0xe4, 0x0e, 0x88, + 0x38, 0x7c, 0x10, 0x04, 0x09, 0x48, 0x83, 0xac, 0x1b, 0x18, 0xf3, 0x44, 0xc1, 0xca, 0x1d, 0x15, + 0x40, 0x8e, 0x05, 0x02, 0x20, 0xe6, 0x24, 0x12, 0x8c, 0x8b, 0x05, 0x21, 0x07, 0x24, 0x14, 0x08, + 0x73, 0x80, 0x19, 0x78, 0x43, 0xb2, 0xff, 0x15, 0x30, 0xc4, 0x01, 0x26, 0x8f, 0x14, 0x61, 0xa9, + 0x8a, 0x09, 0x10, 0x02, 0x12, 0x1c, 0x80, 0x84, 0xaf, 0x10, 0x71, 0xaa, 0xc4, 0x00, 0x3b, 0x04, + 0xea, 0x24, 0x48, 0x1c, 0xbd, 0x8f, 0xf8, 0x00, 0x67, 0xf0, 0x09, 0x40, 0x20, 0x61, 0x00, 0xe4, + 0xf6, 0x07, 0x4b, 0xc1, 0x1f, 0x07, 0x14, 0x40, 0x1c, 0x9d, 0x66, 0x79, 0x24, 0xc6, 0xa0, 0x0e, + 0x32, 0x51, 0xfa, 0xce, 0xe7, 0x50, 0x07, 0x1c, 0x80, 0x30, 0x58, 0x0e, 0xa2, 0xcc, 0xa0, 0x19, + 0x00, 0x71, 0x42, 0x13, 0x27, 0x40, 0xf5, 0x45, 0x41, 0xc5, 0x08, 0xb0, 0x80, 0xc6, 0x18, 0xf2, + 0x28, 0x04, 0x83, 0xe8, 0x58, 0x10, 0x30, 0xc2, 0x2c, 0x40, 0x91, 0x89, 0x3c, 0x88, 0x62, 0x21, + 0xd2, 0xff, 0x03, 0x87, 0xc8, 0x12, 0x19, 0x08, 0x39, 0x3e, 0x83, 0xb2, 0x4a, 0x0e, 0xa2, 0x0d, + 0xc0, 0xe0, 0x50, 0x06, 0xa7, 0xe8, 0x2c, 0x94, 0xc2, 0x09, 0x50, 0x8c, 0xce, 0x20, 0x34, 0x70, + 0x71, 0x41, 0x3e, 0x85, 0xe2, 0xe0, 0x41, 0x38, 0x1e, 0x28, 0x3c, 0x19, 0xc8, 0x70, 0x4f, 0xc1, + 0xdc, 0xe0, 0x74, 0x01, 0xd8, 0xc6, 0x24, 0x00, 0x82, 0x81, 0x7c, 0x12, 0xa6, 0x7e, 0x10, 0x28, + 0xd8, 0x22, 0x00, 0xe3, 0xfc, 0x34, 0x53, 0x00, 0x23, 0x1c, 0x04, 0x44, 0x0e, 0x50, 0x10, 0xeb, + 0x17, 0xca, 0x1c, 0x07, 0x20, +}; +const uint8_t* const _I_DolphinMafia_115x62[] = {_I_DolphinMafia_115x62_0}; + +const uint8_t _I_DolphinNice_96x59_0[] = { + 0x01, 0x00, 0x8a, 0x01, 0x00, 0x37, 0xfa, 0x3e, 0x0a, 0x8f, 0x04, 0x04, 0x02, 0x20, 0xb7, 0x8c, + 0x00, 0x86, 0x1c, 0x0b, 0x78, 0x20, 0x08, 0x66, 0x00, 0xb7, 0x81, 0x00, 0x86, 0x80, 0x0b, 0x71, + 0x61, 0x60, 0x01, 0x4c, 0x07, 0x41, 0xe3, 0x07, 0xd0, 0x4e, 0x40, 0xb8, 0x1f, 0x90, 0x00, 0xe4, + 0x00, 0xba, 0x88, 0x01, 0x0e, 0x10, 0x0a, 0x48, 0xf9, 0x6c, 0xbe, 0x10, 0x70, 0x82, 0x78, 0x3c, + 0x15, 0x82, 0x18, 0xc2, 0x21, 0x00, 0xb4, 0x02, 0x0e, 0xbc, 0x86, 0x30, 0x48, 0x80, 0xd1, 0x05, + 0x03, 0x78, 0x82, 0xc0, 0x3e, 0x52, 0x32, 0x63, 0x70, 0x20, 0x70, 0x09, 0xd4, 0x98, 0xb0, 0xf0, + 0x60, 0x58, 0xc9, 0xce, 0x12, 0x0b, 0xbf, 0xd4, 0x9d, 0x28, 0x9e, 0x24, 0xa9, 0x82, 0xda, 0x24, + 0x2d, 0x10, 0x00, 0xfd, 0x2a, 0x60, 0xb4, 0x85, 0x4e, 0x00, 0x85, 0xf8, 0xd4, 0x82, 0xd2, 0x09, + 0xc0, 0x12, 0x14, 0x12, 0xad, 0x81, 0x29, 0xa8, 0x90, 0xf5, 0x01, 0x75, 0x80, 0x46, 0x00, 0xa5, + 0x50, 0x0b, 0x90, 0x1c, 0x41, 0x63, 0x60, 0x05, 0x96, 0xc0, 0x2e, 0x52, 0x44, 0x79, 0x60, 0x06, + 0x05, 0x50, 0x05, 0x94, 0x89, 0x88, 0x63, 0x02, 0x98, 0x02, 0xc7, 0xc1, 0x21, 0x6a, 0x98, 0xa0, + 0x62, 0x11, 0x00, 0x58, 0xc6, 0x02, 0xe2, 0xb8, 0x21, 0x80, 0xc3, 0x05, 0x02, 0x38, 0x11, 0x78, + 0xa5, 0x0b, 0x01, 0x81, 0x5a, 0x88, 0x2c, 0x60, 0x40, 0xb1, 0xc0, 0x27, 0x0a, 0xfc, 0x0f, 0x28, + 0x04, 0x06, 0x50, 0x05, 0x18, 0xa9, 0x94, 0xc1, 0x67, 0x48, 0x02, 0x8c, 0xb8, 0x16, 0xf8, 0x80, + 0x28, 0xd6, 0x16, 0x86, 0x0b, 0x38, 0x40, 0xd4, 0x76, 0x0c, 0xd4, 0x05, 0x94, 0x10, 0x9a, 0x34, + 0x01, 0x82, 0x1f, 0x06, 0x05, 0x02, 0x98, 0x01, 0x47, 0x54, 0x18, 0x35, 0xc8, 0xff, 0x20, 0x3c, + 0x00, 0x58, 0xd5, 0x6a, 0xa0, 0xb3, 0x81, 0xa3, 0x0a, 0x0f, 0x80, 0xd5, 0xea, 0x81, 0x67, 0x07, + 0x46, 0x14, 0xe3, 0xe1, 0x55, 0x18, 0x18, 0x2c, 0x51, 0x85, 0xc0, 0xef, 0x85, 0x8c, 0x0c, 0x30, + 0xf4, 0x61, 0x40, 0x2d, 0x46, 0xb4, 0x05, 0x8b, 0x04, 0xb0, 0x15, 0x40, 0x5a, 0x50, 0x23, 0xe6, + 0x01, 0x02, 0x8c, 0xa8, 0x2e, 0xb1, 0xe5, 0x40, 0x81, 0x46, 0x6a, 0x17, 0x59, 0xeb, 0xe4, 0xa8, + 0x11, 0xa0, 0x5a, 0x68, 0x27, 0x4e, 0xd3, 0x59, 0xad, 0x82, 0xfa, 0xed, 0x2a, 0x04, 0x28, 0x2e, + 0xb7, 0xa7, 0x69, 0xc3, 0x42, 0xeb, 0xf5, 0x1f, 0x09, 0x4c, 0x42, 0xed, 0xea, 0x01, 0x8c, 0x06, + 0x41, 0x05, 0x0b, 0xbc, 0x02, 0x0d, 0x80, 0x83, 0x05, 0xe2, 0x11, 0x40, 0x0b, 0xb7, 0x14, 0x06, + 0x33, 0x0c, 0x83, 0x89, 0x02, 0xe3, 0xca, 0x3d, 0x95, 0x01, 0xe2, 0x21, 0x74, 0xc2, 0x81, 0x0b, + 0x0e, 0x17, 0x6c, 0x10, 0x10, 0xaf, 0x09, 0xe2, 0x0b, 0xbb, 0xd0, 0x42, 0xeb, 0x02, +}; +const uint8_t* const _I_DolphinNice_96x59[] = {_I_DolphinNice_96x59_0}; + +const uint8_t _I_DolphinWait_61x59_0[] = { + 0x01, 0x00, 0x56, 0x01, 0x00, 0x17, 0xfa, 0x1e, 0x06, 0x4f, 0x84, 0x06, 0xe0, 0x07, 0x48, 0x64, + 0x03, 0x01, 0x01, 0x03, 0x9c, 0x0c, 0x04, 0x30, 0x60, 0x31, 0x70, 0x00, 0x65, 0x08, 0x01, 0x94, + 0xc0, 0x06, 0x51, 0x00, 0x5b, 0x48, 0x00, 0x65, 0x04, 0x01, 0x95, 0x00, 0x82, 0xd8, 0x00, 0x19, + 0x40, 0x7e, 0x00, 0x75, 0x1f, 0x88, 0xe0, 0x88, 0x02, 0x1a, 0x1f, 0x94, 0x14, 0x0e, 0xbf, 0x98, + 0x58, 0x5c, 0x42, 0x45, 0x00, 0x9e, 0x99, 0x87, 0x01, 0x02, 0x11, 0x94, 0xf2, 0x2e, 0x03, 0x18, + 0x39, 0x28, 0x70, 0x1f, 0xc0, 0x3e, 0x42, 0x00, 0xe5, 0x80, 0xff, 0xdf, 0xc0, 0xe5, 0xf8, 0x85, + 0xd8, 0x10, 0x27, 0x40, 0xf9, 0xc2, 0x63, 0x88, 0x12, 0x82, 0x6a, 0x20, 0x50, 0x41, 0xe9, 0x42, + 0x20, 0x95, 0x48, 0x6e, 0x0c, 0xfa, 0x9a, 0xaf, 0xf9, 0x90, 0xe2, 0x10, 0x2e, 0xac, 0xe0, 0x0e, + 0x98, 0x29, 0x52, 0x11, 0x13, 0x23, 0x15, 0x3e, 0x20, 0x3c, 0x61, 0x40, 0x52, 0xfc, 0x4f, 0xe2, + 0x10, 0x38, 0x68, 0x1c, 0xa0, 0xfc, 0x08, 0xbe, 0x04, 0x1e, 0x5e, 0x01, 0xb9, 0x03, 0xc5, 0x60, + 0x24, 0xf2, 0x84, 0x60, 0x63, 0x40, 0x71, 0x27, 0x9c, 0x0e, 0x2b, 0x04, 0x6c, 0xa4, 0x06, 0x15, + 0x08, 0x6c, 0x99, 0x8c, 0xa6, 0x0f, 0x81, 0x00, 0x0c, 0x08, 0xf0, 0x3c, 0x05, 0x61, 0xc0, 0x40, + 0x86, 0xd0, 0x30, 0x78, 0x80, 0x0c, 0xc6, 0x2b, 0x92, 0x00, 0x0d, 0x51, 0xf0, 0x2d, 0x42, 0x0a, + 0x8e, 0xaa, 0x34, 0x0f, 0x4a, 0x85, 0x55, 0x6e, 0x20, 0xf3, 0xd5, 0x6a, 0x84, 0xa2, 0x66, 0x2a, + 0x05, 0xf7, 0xaa, 0x07, 0x18, 0xaf, 0xfb, 0x7f, 0xea, 0xc1, 0xef, 0xc0, 0xe3, 0xea, 0x80, 0xf8, + 0x27, 0xf0, 0x0a, 0xc0, 0x1c, 0x67, 0xa2, 0xd1, 0xb1, 0xc0, 0x34, 0x00, 0x71, 0x14, 0x8f, 0x00, + 0x98, 0x34, 0x02, 0x69, 0xd0, 0x37, 0x90, 0x16, 0xf1, 0x00, 0x06, 0xe1, 0x84, 0x31, 0x89, 0x14, + 0xe9, 0xdc, 0x40, 0x38, 0xa4, 0xc4, 0x4c, 0x3c, 0x1f, 0x88, 0x8c, 0x5b, 0xc3, 0x01, 0xbc, 0x40, + 0x3f, 0xf0, 0xf6, 0x71, 0x0c, 0x0b, 0xe0, 0x07, 0x3c, 0x0a, 0xf8, 0xa3, 0xf0, 0x03, 0xb8, 0xd8, + 0x80, 0xe8, 0x87, 0x1b, 0xa8, 0x1c, 0x78, 0x1f, 0xf8, 0x0e, 0x7e, 0x01, 0x6a, 0x03, 0x94, 0x0f, + 0xfd, 0xa0, 0x80, 0x7d, 0x49, 0x04, 0x4d, 0x12, 0xc0, 0xfa, 0x83, 0x83, 0xbe, 0x26, 0x8d, 0x02, + 0x05, 0xd5, 0xff, 0xff, 0xeb, 0xe9, 0x31, 0x90, 0x40, 0x80, +}; +const uint8_t* const _I_DolphinWait_61x59[] = {_I_DolphinWait_61x59_0}; + +const uint8_t _I_iButtonDolphinVerySuccess_108x52_0[] = { + 0x01, 0x00, 0xc2, 0x01, 0x00, 0x0f, 0xe2, 0xfe, 0x0d, 0xb8, 0x3e, 0x02, 0x06, 0x0c, 0x9f, 0x00, + 0x08, 0x61, 0x80, 0xd9, 0x8c, 0x00, 0x86, 0x60, 0x0d, 0x98, 0x30, 0x08, 0x6a, 0x00, 0xd9, 0x80, + 0x80, 0x87, 0x40, 0x0c, 0x8c, 0x00, 0x0c, 0xa8, 0x01, 0x12, 0x00, 0x2d, 0x00, 0x22, 0x70, 0x20, + 0x6b, 0xc8, 0x02, 0x26, 0x62, 0x88, 0x80, 0x6c, 0xc9, 0x24, 0x0d, 0x9a, 0x07, 0x17, 0xfe, 0x1d, + 0x68, 0x40, 0x6c, 0xe7, 0x48, 0x04, 0x28, 0x10, 0x34, 0xe8, 0x10, 0xd1, 0x11, 0xc4, 0x01, 0xa5, + 0x04, 0x06, 0x96, 0xa0, 0xa6, 0x24, 0xc2, 0x88, 0x17, 0x88, 0x1a, 0x7d, 0x43, 0x78, 0x82, 0x4a, + 0x40, 0x03, 0x20, 0xb0, 0xff, 0x20, 0x16, 0xa3, 0xb2, 0x48, 0x03, 0xe4, 0x0d, 0x1f, 0xfc, 0x06, + 0x3a, 0x0d, 0x4a, 0x00, 0x34, 0xf8, 0x00, 0xd1, 0x37, 0x0f, 0x82, 0x9e, 0x95, 0x58, 0x17, 0x83, + 0xff, 0x81, 0x1b, 0x0f, 0xf1, 0xfe, 0x71, 0xe0, 0x69, 0x7c, 0x3f, 0xe0, 0x82, 0xff, 0xcf, 0xc0, + 0x85, 0x61, 0x80, 0x43, 0xb0, 0x5f, 0xa8, 0x79, 0xdc, 0x81, 0xa5, 0x70, 0xc0, 0x68, 0x3c, 0x10, + 0x1a, 0x17, 0xd5, 0x28, 0x42, 0xd1, 0x8f, 0x84, 0x46, 0x83, 0xb0, 0x8e, 0x40, 0x34, 0x5f, 0xa8, + 0x38, 0x34, 0x45, 0xa2, 0x0d, 0x18, 0x04, 0x9b, 0x50, 0x03, 0x1a, 0x14, 0x35, 0x36, 0x5f, 0x8f, + 0xf8, 0xb8, 0xa4, 0x19, 0x40, 0x18, 0xe8, 0xa0, 0xca, 0x22, 0xfe, 0x7f, 0xc4, 0x05, 0x20, 0xa5, + 0x80, 0xc6, 0x82, 0xcb, 0x3f, 0xf3, 0x44, 0xfc, 0x12, 0x40, 0x18, 0xe8, 0x51, 0x82, 0x52, 0x28, + 0xfc, 0x38, 0x0a, 0x3e, 0x48, 0x98, 0x6c, 0x8f, 0x43, 0x00, 0xe0, 0x63, 0xe0, 0x62, 0xe2, 0x91, + 0x90, 0x0a, 0x02, 0x0d, 0x2f, 0x82, 0x50, 0x41, 0xa3, 0x80, 0x90, 0x41, 0x04, 0xc3, 0x01, 0xc0, + 0x83, 0x46, 0x71, 0x30, 0x06, 0x95, 0x82, 0x21, 0x02, 0x6e, 0x88, 0x6c, 0x43, 0x83, 0x1f, 0x2f, + 0x88, 0x34, 0x62, 0x00, 0xd1, 0x15, 0x08, 0x2c, 0x60, 0xcc, 0x51, 0x0f, 0x08, 0xcc, 0x81, 0xa2, + 0x12, 0x10, 0x68, 0xc6, 0x3f, 0x06, 0xc2, 0x06, 0x8e, 0x02, 0x16, 0x41, 0x20, 0x10, 0xf8, 0x01, + 0x85, 0x00, 0x19, 0x0d, 0x82, 0x18, 0x07, 0x20, 0x81, 0x00, 0x0c, 0x9c, 0x31, 0x08, 0x42, 0x74, + 0x81, 0xab, 0x80, 0x03, 0x0c, 0x32, 0x11, 0x0b, 0x06, 0xb9, 0xc0, 0x43, 0xa3, 0x10, 0x8b, 0x83, + 0x5c, 0xe0, 0x20, 0x81, 0xc8, 0x26, 0x49, 0x4c, 0x40, 0x02, 0x86, 0x0a, 0xc5, 0x22, 0x32, 0x50, + 0x6b, 0x93, 0x86, 0xc0, 0x0d, 0x19, 0x18, 0x35, 0x8c, 0x84, 0x79, 0x1a, 0x84, 0x84, 0x1a, 0xdf, + 0xc2, 0xe0, 0x8a, 0xc7, 0x51, 0x22, 0x06, 0xb5, 0x5e, 0x3f, 0x00, 0x77, 0x0d, 0x60, 0x36, 0xfa, + 0xa9, 0xd7, 0x00, 0x08, 0x3a, 0xc9, 0x02, 0x48, 0xc0, 0x05, 0x54, 0xba, 0x98, 0x8a, 0xa8, 0xf1, + 0x20, 0x6a, 0x6a, 0x3d, 0x43, 0x61, 0x80, 0x4a, 0x81, 0xaf, 0x40, 0xea, 0x8d, 0x86, 0x01, 0x56, + 0x06, 0x93, 0x60, 0x80, 0x05, 0xea, 0x01, 0x94, 0xac, 0x1b, 0x11, 0x80, 0x19, 0x45, 0x41, 0x44, + 0x0d, 0x58, 0x33, 0x18, 0xa1, 0x4f, 0xf3, 0x06, 0x1f, 0x01, 0x76, 0x58, 0x00, 0xd9, 0x83, 0x52, + 0x7c, 0x11, 0x38, 0x51, 0x40, 0x80, +}; +const uint8_t* const _I_iButtonDolphinVerySuccess_108x52[] = { + _I_iButtonDolphinVerySuccess_108x52_0}; + +const uint8_t _I_iButtonKey_49x44_0[] = { + 0x01, 0x00, 0xb4, 0x00, 0x00, 0x24, 0xfc, 0x0a, 0x9c, 0x0e, 0x00, 0x19, 0x26, 0x18, 0x00, 0x32, + 0x43, 0x20, 0x10, 0x10, 0x31, 0xc0, 0x80, 0xc9, 0x80, 0x02, 0x08, 0x18, 0xec, 0x00, 0x21, 0x03, + 0x1c, 0x40, 0x1e, 0x22, 0x15, 0xa0, 0x08, 0x56, 0x40, 0x06, 0x30, 0xc0, 0x85, 0x84, 0x86, 0x40, + 0x21, 0x84, 0x10, 0xcc, 0x04, 0x30, 0x40, 0x31, 0x02, 0x88, 0x3a, 0x20, 0x01, 0x83, 0x0d, 0x94, + 0x06, 0x26, 0x03, 0xf8, 0x43, 0xc5, 0xe9, 0x0c, 0x11, 0x08, 0xbc, 0xe0, 0x64, 0x21, 0x23, 0x09, + 0x38, 0x80, 0x22, 0x28, 0x20, 0x58, 0x99, 0xc4, 0x50, 0x41, 0xe1, 0xc0, 0x60, 0xcc, 0xab, 0x47, + 0x21, 0xa6, 0x02, 0x9e, 0x06, 0x22, 0x70, 0xf0, 0x00, 0xcb, 0x40, 0x03, 0x18, 0xb0, 0x78, 0x14, + 0xe0, 0x32, 0x58, 0x28, 0xa5, 0x84, 0xd0, 0x51, 0x80, 0xc9, 0x30, 0x06, 0xae, 0x62, 0x84, 0x06, + 0x48, 0x64, 0x88, 0x0c, 0x90, 0x29, 0x08, 0x19, 0x30, 0x31, 0x13, 0x71, 0xb8, 0xc4, 0xea, 0x70, + 0x6b, 0xc5, 0x01, 0x4a, 0x7f, 0xc8, 0x7c, 0x81, 0x4a, 0x77, 0x8a, 0xac, 0x45, 0x4a, 0x7f, 0x08, + 0x54, 0x39, 0x4a, 0x7e, 0x0e, 0xa9, 0xf0, 0xcb, 0xe3, 0x7f, 0x6e, 0x22, 0x5c, 0x59, 0x44, 0x00, + 0x28, 0x7a, 0xd4, 0x40, 0x07, 0xf0, 0x02, 0xa0, +}; +const uint8_t* const _I_iButtonKey_49x44[] = {_I_iButtonKey_49x44_0}; + +const Icon I_Certification1_103x56 = { + .width = 103, + .height = 56, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Certification1_103x56}; +const Icon I_Certification2_98x33 = { + .width = 98, + .height = 33, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Certification2_98x33}; +const Icon A_Levelup1_128x64 = + {.width = 128, .height = 64, .frame_count = 11, .frame_rate = 2, .frames = _A_Levelup1_128x64}; +const Icon A_Levelup2_128x64 = + {.width = 128, .height = 64, .frame_count = 11, .frame_rate = 2, .frames = _A_Levelup2_128x64}; +const Icon I_125_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_125_10px}; +const Icon I_Apps_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_Apps_10px}; +const Icon I_Nfc_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_Nfc_10px}; +const Icon I_back_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_back_10px}; +const Icon I_badusb_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_badusb_10px}; +const Icon I_dir_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_dir_10px}; +const Icon I_ibutt_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_ibutt_10px}; +const Icon I_ir_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_ir_10px}; +const Icon I_keyboard_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_keyboard_10px}; +const Icon I_loading_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_loading_10px}; +const Icon I_music_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_music_10px}; +const Icon I_sub1_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_sub1_10px}; +const Icon I_u2f_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_u2f_10px}; +const Icon I_unknown_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_unknown_10px}; +const Icon I_update_10px = + {.width = 10, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_update_10px}; +const Icon I_BLE_Pairing_128x64 = { + .width = 128, + .height = 64, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_BLE_Pairing_128x64}; +const Icon I_Ble_connected_15x15 = { + .width = 15, + .height = 15, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Ble_connected_15x15}; +const Icon I_Ble_disconnected_15x15 = { + .width = 15, + .height = 15, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Ble_disconnected_15x15}; +const Icon I_Button_18x18 = + {.width = 18, .height = 18, .frame_count = 1, .frame_rate = 0, .frames = _I_Button_18x18}; +const Icon I_Circles_47x47 = + {.width = 47, .height = 47, .frame_count = 1, .frame_rate = 0, .frames = _I_Circles_47x47}; +const Icon I_Left_mouse_icon_9x9 = + {.width = 9, .height = 9, .frame_count = 1, .frame_rate = 0, .frames = _I_Left_mouse_icon_9x9}; +const Icon I_Ok_btn_9x9 = + {.width = 9, .height = 9, .frame_count = 1, .frame_rate = 0, .frames = _I_Ok_btn_9x9}; +const Icon I_Ok_btn_pressed_13x13 = { + .width = 13, + .height = 13, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Ok_btn_pressed_13x13}; +const Icon I_Pressed_Button_13x13 = { + .width = 13, + .height = 13, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Pressed_Button_13x13}; +const Icon I_Right_mouse_icon_9x9 = { + .width = 9, + .height = 9, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Right_mouse_icon_9x9}; +const Icon I_Space_65x18 = + {.width = 65, .height = 18, .frame_count = 1, .frame_rate = 0, .frames = _I_Space_65x18}; +const Icon I_Voldwn_6x6 = + {.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Voldwn_6x6}; +const Icon I_Volup_8x6 = + {.width = 8, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Volup_8x6}; +const Icon I_Clock_18x18 = + {.width = 18, .height = 18, .frame_count = 1, .frame_rate = 0, .frames = _I_Clock_18x18}; +const Icon I_Error_18x18 = + {.width = 18, .height = 18, .frame_count = 1, .frame_rate = 0, .frames = _I_Error_18x18}; +const Icon I_EviSmile1_18x21 = + {.width = 18, .height = 21, .frame_count = 1, .frame_rate = 0, .frames = _I_EviSmile1_18x21}; +const Icon I_EviSmile2_18x21 = + {.width = 18, .height = 21, .frame_count = 1, .frame_rate = 0, .frames = _I_EviSmile2_18x21}; +const Icon I_EviWaiting1_18x21 = + {.width = 18, .height = 21, .frame_count = 1, .frame_rate = 0, .frames = _I_EviWaiting1_18x21}; +const Icon I_EviWaiting2_18x21 = + {.width = 18, .height = 21, .frame_count = 1, .frame_rate = 0, .frames = _I_EviWaiting2_18x21}; +const Icon I_Percent_10x14 = + {.width = 10, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_Percent_10x14}; +const Icon I_Smile_18x18 = + {.width = 18, .height = 18, .frame_count = 1, .frame_rate = 0, .frames = _I_Smile_18x18}; +const Icon I_UsbTree_48x22 = + {.width = 48, .height = 22, .frame_count = 1, .frame_rate = 0, .frames = _I_UsbTree_48x22}; +const Icon I_ActiveConnection_50x64 = { + .width = 50, + .height = 64, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_ActiveConnection_50x64}; +const Icon I_ButtonCenter_7x7 = + {.width = 7, .height = 7, .frame_count = 1, .frame_rate = 0, .frames = _I_ButtonCenter_7x7}; +const Icon I_ButtonDown_7x4 = + {.width = 7, .height = 4, .frame_count = 1, .frame_rate = 0, .frames = _I_ButtonDown_7x4}; +const Icon I_ButtonLeftSmall_3x5 = + {.width = 3, .height = 5, .frame_count = 1, .frame_rate = 0, .frames = _I_ButtonLeftSmall_3x5}; +const Icon I_ButtonLeft_4x7 = + {.width = 4, .height = 7, .frame_count = 1, .frame_rate = 0, .frames = _I_ButtonLeft_4x7}; +const Icon I_ButtonRightSmall_3x5 = { + .width = 3, + .height = 5, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_ButtonRightSmall_3x5}; +const Icon I_ButtonRight_4x7 = + {.width = 4, .height = 7, .frame_count = 1, .frame_rate = 0, .frames = _I_ButtonRight_4x7}; +const Icon I_ButtonUp_7x4 = + {.width = 7, .height = 4, .frame_count = 1, .frame_rate = 0, .frames = _I_ButtonUp_7x4}; +const Icon I_DFU_128x50 = + {.width = 128, .height = 50, .frame_count = 1, .frame_rate = 0, .frames = _I_DFU_128x50}; +const Icon I_Warning_30x23 = + {.width = 30, .height = 23, .frame_count = 1, .frame_rate = 0, .frames = _I_Warning_30x23}; +const Icon A_Loading_24 = + {.width = 24, .height = 24, .frame_count = 7, .frame_rate = 5, .frames = _A_Loading_24}; +const Icon A_Round_loader_8x8 = + {.width = 8, .height = 8, .frame_count = 5, .frame_rate = 2, .frames = _A_Round_loader_8x8}; +const Icon I_DolphinCommon_56x48 = { + .width = 56, + .height = 48, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_DolphinCommon_56x48}; +const Icon I_ArrowDownEmpty_14x15 = { + .width = 14, + .height = 15, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_ArrowDownEmpty_14x15}; +const Icon I_ArrowDownFilled_14x15 = { + .width = 14, + .height = 15, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_ArrowDownFilled_14x15}; +const Icon I_ArrowUpEmpty_14x15 = { + .width = 14, + .height = 15, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_ArrowUpEmpty_14x15}; +const Icon I_ArrowUpFilled_14x15 = { + .width = 14, + .height = 15, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_ArrowUpFilled_14x15}; +const Icon I_DolphinReadingSuccess_59x63 = { + .width = 59, + .height = 63, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_DolphinReadingSuccess_59x63}; +const Icon I_Down_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Down_25x27}; +const Icon I_Down_hvr_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Down_hvr_25x27}; +const Icon I_InfraredArrowDown_4x8 = { + .width = 8, + .height = 4, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_InfraredArrowDown_4x8}; +const Icon I_InfraredArrowUp_4x8 = + {.width = 8, .height = 4, .frame_count = 1, .frame_rate = 0, .frames = _I_InfraredArrowUp_4x8}; +const Icon I_InfraredLearnShort_128x31 = { + .width = 128, + .height = 31, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_InfraredLearnShort_128x31}; +const Icon I_Mode_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Mode_25x27}; +const Icon I_Mode_hvr_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Mode_hvr_25x27}; +const Icon I_Mute_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Mute_25x27}; +const Icon I_Mute_hvr_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Mute_hvr_25x27}; +const Icon I_Power_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Power_25x27}; +const Icon I_Power_hvr_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Power_hvr_25x27}; +const Icon I_Rotate_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Rotate_25x27}; +const Icon I_Rotate_hvr_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Rotate_hvr_25x27}; +const Icon I_Swing_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Swing_25x27}; +const Icon I_Swing_hvr_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Swing_hvr_25x27}; +const Icon I_Timer_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Timer_25x27}; +const Icon I_Timer_hvr_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Timer_hvr_25x27}; +const Icon I_Up_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Up_25x27}; +const Icon I_Up_hvr_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Up_hvr_25x27}; +const Icon I_Vol_down_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Vol_down_25x27}; +const Icon I_Vol_down_hvr_25x27 = { + .width = 25, + .height = 27, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Vol_down_hvr_25x27}; +const Icon I_Vol_up_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Vol_up_25x27}; +const Icon I_Vol_up_hvr_25x27 = + {.width = 25, .height = 27, .frame_count = 1, .frame_rate = 0, .frames = _I_Vol_up_hvr_25x27}; +const Icon I_DoorLeft_70x55 = + {.width = 70, .height = 55, .frame_count = 1, .frame_rate = 0, .frames = _I_DoorLeft_70x55}; +const Icon I_DoorRight_70x55 = + {.width = 70, .height = 55, .frame_count = 1, .frame_rate = 0, .frames = _I_DoorRight_70x55}; +const Icon I_SmallArrowDown_3x5 = + {.width = 5, .height = 3, .frame_count = 1, .frame_rate = 0, .frames = _I_SmallArrowDown_3x5}; +const Icon I_SmallArrowDown_4x7 = + {.width = 7, .height = 4, .frame_count = 1, .frame_rate = 0, .frames = _I_SmallArrowDown_4x7}; +const Icon I_SmallArrowUp_3x5 = + {.width = 5, .height = 3, .frame_count = 1, .frame_rate = 0, .frames = _I_SmallArrowUp_3x5}; +const Icon I_SmallArrowUp_4x7 = + {.width = 7, .height = 4, .frame_count = 1, .frame_rate = 0, .frames = _I_SmallArrowUp_4x7}; +const Icon I_WarningDolphin_45x42 = { + .width = 45, + .height = 42, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_WarningDolphin_45x42}; +const Icon I_KeyBackspaceSelected_16x9 = { + .width = 16, + .height = 9, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_KeyBackspaceSelected_16x9}; +const Icon I_KeyBackspace_16x9 = + {.width = 16, .height = 9, .frame_count = 1, .frame_rate = 0, .frames = _I_KeyBackspace_16x9}; +const Icon I_KeySaveSelected_24x11 = { + .width = 24, + .height = 11, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_KeySaveSelected_24x11}; +const Icon I_KeySave_24x11 = + {.width = 24, .height = 11, .frame_count = 1, .frame_rate = 0, .frames = _I_KeySave_24x11}; +const Icon A_125khz_14 = + {.width = 14, .height = 14, .frame_count = 4, .frame_rate = 3, .frames = _A_125khz_14}; +const Icon A_BadUsb_14 = + {.width = 14, .height = 14, .frame_count = 11, .frame_rate = 3, .frames = _A_BadUsb_14}; +const Icon A_Clock_14 = + {.width = 14, .height = 14, .frame_count = 8, .frame_rate = 3, .frames = _A_Clock_14}; +const Icon A_Debug_14 = + {.width = 14, .height = 14, .frame_count = 4, .frame_rate = 3, .frames = _A_Debug_14}; +const Icon A_FileManager_14 = + {.width = 14, .height = 14, .frame_count = 10, .frame_rate = 3, .frames = _A_FileManager_14}; +const Icon A_GPIO_14 = + {.width = 14, .height = 14, .frame_count = 8, .frame_rate = 3, .frames = _A_GPIO_14}; +const Icon A_Infrared_14 = + {.width = 14, .height = 14, .frame_count = 6, .frame_rate = 3, .frames = _A_Infrared_14}; +const Icon A_NFC_14 = + {.width = 14, .height = 14, .frame_count = 4, .frame_rate = 3, .frames = _A_NFC_14}; +const Icon A_Plugins_14 = + {.width = 14, .height = 14, .frame_count = 9, .frame_rate = 3, .frames = _A_Plugins_14}; +const Icon A_Settings_14 = + {.width = 14, .height = 14, .frame_count = 10, .frame_rate = 3, .frames = _A_Settings_14}; +const Icon A_Sub1ghz_14 = + {.width = 14, .height = 14, .frame_count = 6, .frame_rate = 3, .frames = _A_Sub1ghz_14}; +const Icon A_U2F_14 = + {.width = 14, .height = 14, .frame_count = 4, .frame_rate = 3, .frames = _A_U2F_14}; +const Icon A_UniRFRemix_14 = + {.width = 14, .height = 14, .frame_count = 8, .frame_rate = 4, .frames = _A_UniRFRemix_14}; +const Icon A_iButton_14 = + {.width = 14, .height = 14, .frame_count = 7, .frame_rate = 3, .frames = _A_iButton_14}; +const Icon I_ArrowC_1_36x36 = + {.width = 36, .height = 36, .frame_count = 1, .frame_rate = 0, .frames = _I_ArrowC_1_36x36}; +const Icon I_Detailed_chip_17x13 = { + .width = 17, + .height = 13, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Detailed_chip_17x13}; +const Icon I_Keychain_39x36 = + {.width = 39, .height = 36, .frame_count = 1, .frame_rate = 0, .frames = _I_Keychain_39x36}; +const Icon I_Medium_chip_22x21 = + {.width = 22, .height = 21, .frame_count = 1, .frame_rate = 0, .frames = _I_Medium_chip_22x21}; +const Icon I_Modern_reader_18x34 = { + .width = 18, + .height = 34, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Modern_reader_18x34}; +const Icon I_Move_flipper_26x39 = { + .width = 26, + .height = 39, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Move_flipper_26x39}; +const Icon I_NFC_dolphin_emulation_47x61 = { + .width = 47, + .height = 61, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_NFC_dolphin_emulation_47x61}; +const Icon I_NFC_manual_60x50 = + {.width = 60, .height = 50, .frame_count = 1, .frame_rate = 0, .frames = _I_NFC_manual_60x50}; +const Icon I_Release_arrow_18x15 = { + .width = 18, + .height = 15, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Release_arrow_18x15}; +const Icon I_Restoring_38x32 = + {.width = 38, .height = 32, .frame_count = 1, .frame_rate = 0, .frames = _I_Restoring_38x32}; +const Icon I_Tap_reader_36x38 = + {.width = 36, .height = 38, .frame_count = 1, .frame_rate = 0, .frames = _I_Tap_reader_36x38}; +const Icon I_Pin_arrow_down_7x9 = + {.width = 7, .height = 9, .frame_count = 1, .frame_rate = 0, .frames = _I_Pin_arrow_down_7x9}; +const Icon I_Pin_arrow_left_9x7 = + {.width = 9, .height = 7, .frame_count = 1, .frame_rate = 0, .frames = _I_Pin_arrow_left_9x7}; +const Icon I_Pin_arrow_right_9x7 = + {.width = 9, .height = 7, .frame_count = 1, .frame_rate = 0, .frames = _I_Pin_arrow_right_9x7}; +const Icon I_Pin_arrow_up_7x9 = + {.width = 7, .height = 9, .frame_count = 1, .frame_rate = 0, .frames = _I_Pin_arrow_up_7x9}; +const Icon I_Pin_attention_dpad_29x29 = { + .width = 29, + .height = 29, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Pin_attention_dpad_29x29}; +const Icon I_Pin_back_arrow_10x8 = { + .width = 10, + .height = 8, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Pin_back_arrow_10x8}; +const Icon I_Pin_back_full_40x8 = + {.width = 40, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Pin_back_full_40x8}; +const Icon I_Pin_cell_13x13 = + {.width = 13, .height = 13, .frame_count = 1, .frame_rate = 0, .frames = _I_Pin_cell_13x13}; +const Icon I_Pin_pointer_5x3 = + {.width = 5, .height = 3, .frame_count = 1, .frame_rate = 0, .frames = _I_Pin_pointer_5x3}; +const Icon I_Pin_star_7x7 = + {.width = 7, .height = 7, .frame_count = 1, .frame_rate = 0, .frames = _I_Pin_star_7x7}; +const Icon I_passport_bad1_46x49 = { + .width = 46, + .height = 49, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_passport_bad1_46x49}; +const Icon I_passport_bad2_46x49 = { + .width = 46, + .height = 49, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_passport_bad2_46x49}; +const Icon I_passport_bad3_46x49 = { + .width = 46, + .height = 49, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_passport_bad3_46x49}; +const Icon I_passport_bottom_128x18 = { + .width = 128, + .height = 18, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_passport_bottom_128x18}; +const Icon I_passport_happy1_46x49 = { + .width = 46, + .height = 49, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_passport_happy1_46x49}; +const Icon I_passport_happy2_46x49 = { + .width = 46, + .height = 49, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_passport_happy2_46x49}; +const Icon I_passport_happy3_46x49 = { + .width = 46, + .height = 49, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_passport_happy3_46x49}; +const Icon I_passport_left_6x46 = + {.width = 6, .height = 46, .frame_count = 1, .frame_rate = 0, .frames = _I_passport_left_6x46}; +const Icon I_passport_okay1_46x49 = { + .width = 46, + .height = 49, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_passport_okay1_46x49}; +const Icon I_passport_okay2_46x49 = { + .width = 46, + .height = 49, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_passport_okay2_46x49}; +const Icon I_passport_okay3_46x49 = { + .width = 46, + .height = 49, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_passport_okay3_46x49}; +const Icon I_BatteryBody_52x28 = + {.width = 52, .height = 28, .frame_count = 1, .frame_rate = 0, .frames = _I_BatteryBody_52x28}; +const Icon I_Battery_16x16 = + {.width = 16, .height = 16, .frame_count = 1, .frame_rate = 0, .frames = _I_Battery_16x16}; +const Icon I_FaceCharging_29x14 = { + .width = 29, + .height = 14, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_FaceCharging_29x14}; +const Icon I_FaceConfused_29x14 = { + .width = 29, + .height = 14, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_FaceConfused_29x14}; +const Icon I_FaceNopower_29x14 = + {.width = 29, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_FaceNopower_29x14}; +const Icon I_FaceNormal_29x14 = + {.width = 29, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_FaceNormal_29x14}; +const Icon I_Health_16x16 = + {.width = 16, .height = 16, .frame_count = 1, .frame_rate = 0, .frames = _I_Health_16x16}; +const Icon I_Temperature_16x16 = + {.width = 16, .height = 16, .frame_count = 1, .frame_rate = 0, .frames = _I_Temperature_16x16}; +const Icon I_Unplug_bg_bottom_128x10 = { + .width = 128, + .height = 10, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Unplug_bg_bottom_128x10}; +const Icon I_Unplug_bg_top_128x14 = { + .width = 128, + .height = 14, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Unplug_bg_top_128x14}; +const Icon I_Voltage_16x16 = + {.width = 16, .height = 16, .frame_count = 1, .frame_rate = 0, .frames = _I_Voltage_16x16}; +const Icon I_RFIDDolphinReceive_97x61 = { + .width = 97, + .height = 61, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_RFIDDolphinReceive_97x61}; +const Icon I_RFIDDolphinSend_97x61 = { + .width = 97, + .height = 61, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_RFIDDolphinSend_97x61}; +const Icon I_RFIDDolphinSuccess_108x57 = { + .width = 108, + .height = 57, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_RFIDDolphinSuccess_108x57}; +const Icon I_RFIDSmallChip_14x14 = { + .width = 13, + .height = 13, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_RFIDSmallChip_14x14}; +const Icon I_SDQuestion_35x43 = + {.width = 35, .height = 43, .frame_count = 1, .frame_rate = 0, .frames = _I_SDQuestion_35x43}; +const Icon I_Cry_dolph_55x52 = + {.width = 55, .height = 52, .frame_count = 1, .frame_rate = 0, .frames = _I_Cry_dolph_55x52}; +const Icon I_Alert_9x8 = + {.width = 9, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Alert_9x8}; +const Icon I_Attention_5x8 = + {.width = 5, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Attention_5x8}; +const Icon I_Background_128x11 = { + .width = 128, + .height = 11, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Background_128x11}; +const Icon I_Battery_26x8 = + {.width = 26, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Battery_26x8}; +const Icon I_Bluetooth_Connected_16x8 = { + .width = 16, + .height = 8, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Bluetooth_Connected_16x8}; +const Icon I_Bluetooth_Idle_5x8 = + {.width = 5, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Bluetooth_Idle_5x8}; +const Icon I_Charging_lightning_9x10 = { + .width = 9, + .height = 10, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Charging_lightning_9x10}; +const Icon I_Charging_lightning_mask_9x10 = { + .width = 9, + .height = 10, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_Charging_lightning_mask_9x10}; +const Icon I_GameMode_11x8 = + {.width = 11, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_GameMode_11x8}; +const Icon I_Hidden_window_9x8 = + {.width = 9, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Hidden_window_9x8}; +const Icon I_Lock_8x8 = + {.width = 8, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Lock_8x8}; +const Icon I_SDcardFail_11x8 = + {.width = 11, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_SDcardFail_11x8}; +const Icon I_SDcardMounted_11x8 = + {.width = 11, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_SDcardMounted_11x8}; +const Icon I_Dynamic_9x7 = + {.width = 9, .height = 7, .frame_count = 1, .frame_rate = 0, .frames = _I_Dynamic_9x7}; +const Icon I_Lock_7x8 = + {.width = 7, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Lock_7x8}; +const Icon I_MHz_25x11 = + {.width = 25, .height = 11, .frame_count = 1, .frame_rate = 0, .frames = _I_MHz_25x11}; +const Icon I_Quest_7x8 = + {.width = 7, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Quest_7x8}; +const Icon I_Raw_9x7 = + {.width = 9, .height = 7, .frame_count = 1, .frame_rate = 0, .frames = _I_Raw_9x7}; +const Icon I_Scanning_123x52 = + {.width = 123, .height = 52, .frame_count = 1, .frame_rate = 0, .frames = _I_Scanning_123x52}; +const Icon I_Static_9x7 = + {.width = 9, .height = 7, .frame_count = 1, .frame_rate = 0, .frames = _I_Static_9x7}; +const Icon I_Unlock_7x8 = + {.width = 7, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Unlock_7x8}; +const Icon I_Auth_62x31 = + {.width = 62, .height = 31, .frame_count = 1, .frame_rate = 0, .frames = _I_Auth_62x31}; +const Icon I_Connect_me_62x31 = + {.width = 62, .height = 31, .frame_count = 1, .frame_rate = 0, .frames = _I_Connect_me_62x31}; +const Icon I_Connected_62x31 = + {.width = 62, .height = 31, .frame_count = 1, .frame_rate = 0, .frames = _I_Connected_62x31}; +const Icon I_Drive_112x35 = + {.width = 112, .height = 35, .frame_count = 1, .frame_rate = 0, .frames = _I_Drive_112x35}; +const Icon I_Error_62x31 = + {.width = 62, .height = 31, .frame_count = 1, .frame_rate = 0, .frames = _I_Error_62x31}; +const Icon I_Updating_32x40 = + {.width = 32, .height = 40, .frame_count = 1, .frame_rate = 0, .frames = _I_Updating_32x40}; +const Icon I_DolphinMafia_115x62 = { + .width = 115, + .height = 62, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_DolphinMafia_115x62}; +const Icon I_DolphinNice_96x59 = + {.width = 96, .height = 59, .frame_count = 1, .frame_rate = 0, .frames = _I_DolphinNice_96x59}; +const Icon I_DolphinWait_61x59 = + {.width = 61, .height = 59, .frame_count = 1, .frame_rate = 0, .frames = _I_DolphinWait_61x59}; +const Icon I_iButtonDolphinVerySuccess_108x52 = { + .width = 108, + .height = 52, + .frame_count = 1, + .frame_rate = 0, + .frames = _I_iButtonDolphinVerySuccess_108x52}; +const Icon I_iButtonKey_49x44 = + {.width = 49, .height = 44, .frame_count = 1, .frame_rate = 0, .frames = _I_iButtonKey_49x44}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/LICENSE.md b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/LICENSE.md new file mode 100644 index 000000000..aff89d831 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/LICENSE.md @@ -0,0 +1,21 @@ +License + +Copyright (c) 2022 Mikhail Gubenko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/README.md new file mode 100644 index 000000000..aaaadad9b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/README.md @@ -0,0 +1,36 @@ +![Banner Image](docs/generic-screenshot.png) +# Flipp Pomodoro + +Your productivity best friend. Don't let your flipper get borred, let him help you instead. + +## Development + +### Current state and plans + +At the moment following functionality implemented: +![Working Screen](docs/working.png) +![Resting Screen](docs/resting.png) +* Generic pomodoro cycle with two stages (Work: 25 min and rest 5 minutes) +* Automatic and manual phases switch +* Notification(sound, vibration, backlight, led) on stage change. +* Energy Saving workflow + +Will do(if I've got time): +* ~~Publish a .fap package to let anyone download and install the app.~~ +* * Done, now you can [DOWNLOAD application from releases](https://github.com/Th3Un1q3/flipp_pomodoro/releases) +* Configure CI pipeline for automatic releases +* Stats on exit(how many pomodoros complete) +* Background work or restore from last state +* Integration with passport to develop your flipper profile by completing pomodoros +* Configuration of notifications +* Blind mode(no timer updates, just background and notification) for more energy saving + +### Build and Package +Build application +```shell +# For standard(official) firmware +bash tools/build.sh + +# For unleashed firmware +bash tools/build.sh -f unleashed +``` diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/application.fam new file mode 100644 index 000000000..21e54748b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/application.fam @@ -0,0 +1,11 @@ +App( + appid="flipp_pomodoro", + name="Flipp Pomodoro", + apptype=FlipperAppType.EXTERNAL, + entry_point="flipp_pomodoro_app", + requires=["gui", "notification", "dolphin"], + stack_size=1 * 1024, + fap_category="Misc_Extra", + fap_icon_assets="images", + fap_icon="flipp_pomodoro_10.png", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/flipp_pomodoro_10.png b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/flipp_pomodoro_10.png new file mode 100644 index 000000000..977d16a58 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/flipp_pomodoro_10.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/flipp_pomodoro_app.c b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/flipp_pomodoro_app.c new file mode 100644 index 000000000..c7c61035b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/flipp_pomodoro_app.c @@ -0,0 +1,101 @@ +#include "flipp_pomodoro_app_i.h" + +enum +{ + CustomEventConsumed = true, + CustomEventNotConsumed = false, +}; + +static bool flipp_pomodoro_app_back_event_callback(void *ctx) +{ + furi_assert(ctx); + FlippPomodoroApp *app = ctx; + return scene_manager_handle_back_event(app->scene_manager); +}; + +static void flipp_pomodoro_app_tick_event_callback(void *ctx) +{ + furi_assert(ctx); + FlippPomodoroApp *app = ctx; + + scene_manager_handle_custom_event(app->scene_manager, FlippPomodoroAppCustomEventTimerTick); +}; + +static bool flipp_pomodoro_app_custom_event_callback(void *ctx, uint32_t event) +{ + furi_assert(ctx); + FlippPomodoroApp *app = ctx; + + switch (event) + { + case FlippPomodoroAppCustomEventStageSkip: + flipp_pomodoro__toggle_stage(app->state); + return CustomEventConsumed; + case FlippPomodoroAppCustomEventStageComplete: + if (app->state->stage == Work) + { + // REGISTER a deed on work stage complete to get an acheivement + DOLPHIN_DEED(DolphinDeedPluginGameWin); + }; + + flipp_pomodoro__toggle_stage(app->state); + notification_message(app->notification_app, stage_start_notification_sequence_map[app->state->stage]); + return CustomEventConsumed; + default: + break; + } + return scene_manager_handle_custom_event(app->scene_manager, event); +}; + +FlippPomodoroApp *flipp_pomodoro_app_alloc() +{ + FlippPomodoroApp *app = malloc(sizeof(FlippPomodoroApp)); + app->state = flipp_pomodoro__new(); + + app->scene_manager = scene_manager_alloc(&flipp_pomodoro_scene_handlers, app); + app->gui = furi_record_open(RECORD_GUI); + app->notification_app = furi_record_open(RECORD_NOTIFICATION); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback(app->view_dispatcher, flipp_pomodoro_app_custom_event_callback); + view_dispatcher_set_tick_event_callback(app->view_dispatcher, flipp_pomodoro_app_tick_event_callback, 1000); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_set_navigation_event_callback(app->view_dispatcher, flipp_pomodoro_app_back_event_callback); + + app->timer_view = flipp_pomodoro_view_timer_alloc(); + + view_dispatcher_add_view( + app->view_dispatcher, + FlippPomodoroAppViewTimer, + flipp_pomodoro_view_timer_get_view(app->timer_view)); + + scene_manager_next_scene(app->scene_manager, FlippPomodoroSceneTimer); + + return app; +}; + +void flipp_pomodoro_app_free(FlippPomodoroApp *app) +{ + view_dispatcher_remove_view(app->view_dispatcher, FlippPomodoroAppViewTimer); + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + flipp_pomodoro_view_timer_free(app->timer_view); + flipp_pomodoro__destroy(app->state); + free(app); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); +}; + +int32_t flipp_pomodoro_app(void *p) +{ + UNUSED(p); + FlippPomodoroApp *app = flipp_pomodoro_app_alloc(); + + view_dispatcher_run(app->view_dispatcher); + + flipp_pomodoro_app_free(app); + + return 0; +}; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/flipp_pomodoro_app.h b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/flipp_pomodoro_app.h new file mode 100644 index 000000000..b3e4e9601 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/flipp_pomodoro_app.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "views/flipp_pomodoro_timer_view.h" + +#include "modules/flipp_pomodoro.h" + +typedef enum +{ + // Reserve first 100 events for button types and indexes, starting from 0 + FlippPomodoroAppCustomEventStageSkip = 100, + FlippPomodoroAppCustomEventStageComplete, // By Expiration + FlippPomodoroAppCustomEventTimerTick, +} FlippPomodoroAppCustomEvent; + +typedef struct +{ + SceneManager *scene_manager; + ViewDispatcher *view_dispatcher; + Gui *gui; + NotificationApp *notification_app; + FlippPomodoroTimerView *timer_view; + FlippPomodoroState *state; +} FlippPomodoroApp; + +typedef enum +{ + FlippPomodoroAppViewTimer, +} FlippPomodoroAppView; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/flipp_pomodoro_app_i.h b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/flipp_pomodoro_app_i.h new file mode 100644 index 000000000..492ef9c2d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/flipp_pomodoro_app_i.h @@ -0,0 +1,31 @@ +#pragma once + +#define FURI_DEBUG 1 + +/** + * Index of dependencies for the main app + */ + +// Platform Imports + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// App resource imports + +#include "helpers/time.h" +#include "helpers/notifications.h" +#include "modules/flipp_pomodoro.h" +#include "flipp_pomodoro_app.h" +#include "scenes/flipp_pomodoro_scene.h" +#include "views/flipp_pomodoro_timer_view.h" + +// Auto-compiled icons +#include "flipp_pomodoro_icons.h" diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/debug.h b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/debug.h new file mode 100644 index 000000000..13b8f2998 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/debug.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +#define TAG "FlippPomodoro" \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/notifications.c b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/notifications.c new file mode 100644 index 000000000..388a3f11d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/notifications.c @@ -0,0 +1,49 @@ +#include + +const NotificationSequence work_start_notification = { + &message_display_backlight_on, + + &message_vibro_on, + + &message_note_b5, + &message_delay_250, + + &message_note_d5, + &message_delay_250, + + &message_sound_off, + &message_vibro_off, + + &message_green_255, + &message_delay_1000, + &message_green_0, + &message_delay_250, + &message_green_255, + &message_delay_1000, + + NULL, +}; + +const NotificationSequence rest_start_notification = { + &message_display_backlight_on, + + &message_vibro_on, + + &message_note_d5, + &message_delay_250, + + &message_note_b5, + &message_delay_250, + + &message_sound_off, + &message_vibro_off, + + &message_red_255, + &message_delay_1000, + &message_red_0, + &message_delay_250, + &message_red_255, + &message_delay_1000, + + NULL, +}; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/notifications.h b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/notifications.h new file mode 100644 index 000000000..a66536bb5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/notifications.h @@ -0,0 +1,13 @@ +#pragma once + +#include "../modules/flipp_pomodoro.h" +#include + +extern const NotificationSequence work_start_notification; +extern const NotificationSequence rest_start_notification; + +/// @brief Defines a notification sequence that should indicate start of specific pomodoro stage. +const NotificationSequence *stage_start_notification_sequence_map[] = { + [Work] = &work_start_notification, + [Rest] = &rest_start_notification, +}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/time.c b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/time.c new file mode 100644 index 000000000..eb8de0024 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/time.c @@ -0,0 +1,21 @@ +#include +#include +#include "time.h" + +const int TIME_SECONDS_IN_MINUTE = 60; +const int TIME_MINUTES_IN_HOUR = 60; + +uint32_t time_now() +{ + return furi_hal_rtc_get_timestamp(); +}; + +TimeDifference time_difference_seconds(uint32_t begin, uint32_t end) +{ + const uint32_t duration_seconds = end - begin; + + uint32_t minutes = (duration_seconds / TIME_MINUTES_IN_HOUR) % TIME_MINUTES_IN_HOUR; + uint32_t seconds = duration_seconds % TIME_SECONDS_IN_MINUTE; + + return (TimeDifference){.total_seconds = duration_seconds, .minutes = minutes, .seconds = seconds}; +}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/time.h b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/time.h new file mode 100644 index 000000000..65b7056f9 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/helpers/time.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +extern const int TIME_SECONDS_IN_MINUTE; +extern const int TIME_MINUTES_IN_HOUR; + +/// @brief Container for a time period +typedef struct +{ + uint8_t seconds; + uint8_t minutes; + uint32_t total_seconds; +} TimeDifference; + +/// @brief Time by the moment of calling +/// @return A timestamp(seconds percision) +uint32_t time_now(); + +/// @brief Calculates difference between two provided timestamps +/// @param begin - start timestamp of the period +/// @param end - end timestamp of the period to measure +/// @return TimeDifference struct +TimeDifference time_difference_seconds(uint32_t begin, uint32_t end); diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/images/flipp_pomodoro_rest_64.png b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/images/flipp_pomodoro_rest_64.png new file mode 100644 index 000000000..072affef3 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/images/flipp_pomodoro_rest_64.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/images/flipp_pomodoro_work_64.png b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/images/flipp_pomodoro_work_64.png new file mode 100644 index 000000000..0e36886f2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/images/flipp_pomodoro_work_64.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/modules/flipp_pomodoro.c b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/modules/flipp_pomodoro.c new file mode 100644 index 000000000..718d4b485 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/modules/flipp_pomodoro.c @@ -0,0 +1,72 @@ +#include +#include +#include "../helpers/time.h" +#include "flipp_pomodoro.h" + +char *next_stage_label[] = { + [Work] = "Get Rest", + [Rest] = "Start Work", +}; + +const PomodoroStage stage_rotaion_map[] = { + [Work] = Rest, + [Rest] = Work, +}; + +const PomodoroStage default_stage = Work; + +void flipp_pomodoro__toggle_stage(FlippPomodoroState *state) +{ + furi_assert(state); + state->stage = stage_rotaion_map[state->stage]; + state->started_at_timestamp = time_now(); +}; + +char *flipp_pomodoro__next_stage_label(FlippPomodoroState *state) +{ + furi_assert(state); + return next_stage_label[state->stage]; +}; + +void flipp_pomodoro__destroy(FlippPomodoroState *state) +{ + furi_assert(state); + free(state); +}; + +uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState *state) +{ + const int32_t stage_duration_seconds_map[] = { + [Work] = 25 * TIME_SECONDS_IN_MINUTE, + [Rest] = 5 * TIME_SECONDS_IN_MINUTE, + }; + + return stage_duration_seconds_map[state->stage]; +}; + +uint32_t flipp_pomodoro__stage_expires_timestamp(FlippPomodoroState *state) +{ + return state->started_at_timestamp + flipp_pomodoro__current_stage_total_duration(state); +}; + +TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState *state) +{ + const uint32_t stage_ends_at = flipp_pomodoro__stage_expires_timestamp(state); + return time_difference_seconds(time_now(), stage_ends_at); +}; + +bool flipp_pomodoro__is_stage_expired(FlippPomodoroState *state) +{ + const uint32_t expired_by = flipp_pomodoro__stage_expires_timestamp(state); + const uint8_t seamless_change_span_seconds = 1; + return (time_now() - seamless_change_span_seconds) >= expired_by; +}; + +FlippPomodoroState *flipp_pomodoro__new() +{ + FlippPomodoroState *state = malloc(sizeof(FlippPomodoroState)); + const uint32_t now = time_now(); + state->started_at_timestamp = now; + state->stage = default_stage; + return state; +}; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/modules/flipp_pomodoro.h b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/modules/flipp_pomodoro.h new file mode 100644 index 000000000..3eb67d074 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/modules/flipp_pomodoro.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include "../helpers/time.h" + +/// @brief Options of pomodoro stages +typedef enum +{ + Work, + Rest, +} PomodoroStage; + +/// @brief State of the pomodoro timer +typedef struct +{ + PomodoroStage stage; + uint32_t started_at_timestamp; +} FlippPomodoroState; + +/// @brief Generates initial state +/// @param state - pointer to the state of pomorodo. +/// @returns A new pre-populated state for pomodoro timer +FlippPomodoroState *flipp_pomodoro__new(); + +/// @brief Destroys state of timer and it's dependencies +void flipp_pomodoro__destroy(FlippPomodoroState *state); + +/// @brief Get remaining stage time. +/// @param state - pointer to the state of pomorodo. +/// @returns Time difference to the end of current stage +TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState *state); + +/// @brief Label of transition to the next stage +/// @param state - pointer to the state of pomorodo. +/// @returns string with the label of the "skipp" button +char *flipp_pomodoro__next_stage_label(FlippPomodoroState *state); + +/// @brief Check if current stage is expired +/// @param state - pointer to the state of pomorodo. +/// @returns expriations status - true means stage is expired +bool flipp_pomodoro__is_stage_expired(FlippPomodoroState *state); + +/// @brief Rotate stage of the timer +/// @param state - pointer to the state of pomorodo. +void flipp_pomodoro__toggle_stage(FlippPomodoroState *state); diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/.keep b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/config/flipp_pomodoro_scene_config.h b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/config/flipp_pomodoro_scene_config.h new file mode 100644 index 000000000..f95daeb30 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/config/flipp_pomodoro_scene_config.h @@ -0,0 +1 @@ +ADD_SCENE(flipp_pomodoro, timer, Timer) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/flipp_pomodoro_scene.c b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/flipp_pomodoro_scene.c new file mode 100644 index 000000000..4c611672e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/flipp_pomodoro_scene.c @@ -0,0 +1,30 @@ +#include "flipp_pomodoro_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const flipp_pomodoro_scene_on_enter_handlers[])(void*) = { +#include "config/flipp_pomodoro_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const flipp_pomodoro_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "config/flipp_pomodoro_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const flipp_pomodoro_scene_on_exit_handlers[])(void* context) = { +#include "config/flipp_pomodoro_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers flipp_pomodoro_scene_handlers = { + .on_enter_handlers = flipp_pomodoro_scene_on_enter_handlers, + .on_event_handlers = flipp_pomodoro_scene_on_event_handlers, + .on_exit_handlers = flipp_pomodoro_scene_on_exit_handlers, + .scene_num = FlippPomodoroSceneNum, +}; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/flipp_pomodoro_scene.h b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/flipp_pomodoro_scene.h new file mode 100644 index 000000000..fe4bf35e2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/flipp_pomodoro_scene.h @@ -0,0 +1,28 @@ +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FlippPomodoroScene##id, +typedef enum +{ +#include "config/flipp_pomodoro_scene_config.h" + FlippPomodoroSceneNum, +} FlippPomodoroScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers flipp_pomodoro_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void *); +#include "config/flipp_pomodoro_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void *context, SceneManagerEvent event); +#include "config/flipp_pomodoro_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void *context); +#include "config/flipp_pomodoro_scene_config.h" +#undef ADD_SCENE diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/flipp_pomodoro_scene_timer.c b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/flipp_pomodoro_scene_timer.c new file mode 100644 index 000000000..9e1e03266 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/scenes/flipp_pomodoro_scene_timer.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include "../flipp_pomodoro_app.h" +#include "../views/flipp_pomodoro_timer_view.h" + +enum +{ + SceneEventConusmed = true, + SceneEventNotConusmed = false +}; + +uint8_t ExitSignal = 0; + +void flipp_pomodoro_scene_timer_on_next_stage(void *ctx) +{ + furi_assert(ctx); + + FlippPomodoroApp *app = ctx; + + view_dispatcher_send_custom_event( + app->view_dispatcher, + FlippPomodoroAppCustomEventStageSkip); +}; + +void flipp_pomodoro_scene_timer_on_enter(void *context) +{ + furi_assert(context); + + FlippPomodoroApp *app = context; + + view_dispatcher_switch_to_view(app->view_dispatcher, FlippPomodoroAppViewTimer); + flipp_pomodoro_view_timer_set_state( + flipp_pomodoro_view_timer_get_view(app->timer_view), + app->state); + flipp_pomodoro_view_timer_set_on_right_cb( + app->timer_view, + flipp_pomodoro_scene_timer_on_next_stage, + app); +}; + +void flipp_pomodoro_scene_timer_handle_custom_event(FlippPomodoroApp *app, FlippPomodoroAppCustomEvent custom_event) +{ + if (custom_event == FlippPomodoroAppCustomEventTimerTick && flipp_pomodoro__is_stage_expired(app->state)) + { + view_dispatcher_send_custom_event( + app->view_dispatcher, + FlippPomodoroAppCustomEventStageComplete); + } +}; + +bool flipp_pomodoro_scene_timer_on_event(void *ctx, SceneManagerEvent event) +{ + furi_assert(ctx); + FlippPomodoroApp *app = ctx; + + switch (event.type) + { + case SceneManagerEventTypeCustom: + flipp_pomodoro_scene_timer_handle_custom_event( + app, + event.event); + return SceneEventConusmed; + case SceneManagerEventTypeBack: + return ExitSignal; + default: + break; + }; + return SceneEventNotConusmed; +}; + +void flipp_pomodoro_scene_timer_on_exit(void *context) +{ + UNUSED(context); +}; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/views/.keep b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/views/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/views/flipp_pomodoro_timer_view.c b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/views/flipp_pomodoro_timer_view.c new file mode 100644 index 000000000..826e9e36c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/views/flipp_pomodoro_timer_view.c @@ -0,0 +1,151 @@ +#include "flipp_pomodoro_timer_view.h" +#include +#include +#include +#include +#include "../helpers/debug.h" +#include "../flipp_pomodoro_app.h" +#include "../modules/flipp_pomodoro.h" + +// Auto-compiled icons +#include "flipp_pomodoro_icons.h" + +enum +{ + ViewInputConsumed = true, + ViewInputNotConusmed = false, +}; + +struct FlippPomodoroTimerView +{ + View *view; + FlippPomodoroTimerViewInputCb right_cb; + void *right_cb_ctx; +}; + +typedef struct +{ + FlippPomodoroState *state; +} FlippPomodoroTimerViewModel; + +static const Icon *stage_background_image[] = { + [Work] = &I_flipp_pomodoro_work_64, + [Rest] = &I_flipp_pomodoro_rest_64, +}; + +static void flipp_pomodoro_view_timer_draw_countdown(Canvas *canvas, TimeDifference remaining_time) +{ + canvas_set_font(canvas, FontBigNumbers); + const uint8_t right_border_margin = 1; + + const uint8_t countdown_box_height = canvas_height(canvas) * 0.4; + const uint8_t countdown_box_width = canvas_width(canvas) * 0.5; + const uint8_t countdown_box_x = canvas_width(canvas) - countdown_box_width - right_border_margin; + const uint8_t countdown_box_y = 0; + + elements_bold_rounded_frame(canvas, + countdown_box_x, + countdown_box_y, + countdown_box_width, + countdown_box_height); + + FuriString *timer_string = furi_string_alloc(); + furi_string_printf(timer_string, "%02u:%02u", remaining_time.minutes, remaining_time.seconds); + const char *remaining_stage_time_string = furi_string_get_cstr(timer_string); + canvas_draw_str_aligned( + canvas, + countdown_box_x + (countdown_box_width / 2), + countdown_box_y + (countdown_box_height / 2), + AlignCenter, + AlignCenter, + remaining_stage_time_string); + + furi_string_free(timer_string); +}; + +static void flipp_pomodoro_view_timer_draw_callback(Canvas *canvas, void *_model) +{ + if (!_model) + { + return; + }; + + FlippPomodoroTimerViewModel *model = _model; + + canvas_clear(canvas); + canvas_draw_icon(canvas, 0, 0, stage_background_image[model->state->stage]); + flipp_pomodoro_view_timer_draw_countdown( + canvas, + flipp_pomodoro__stage_remaining_duration(model->state)); + + canvas_set_font(canvas, FontSecondary); + elements_button_right(canvas, flipp_pomodoro__next_stage_label(model->state)); +}; + +bool flipp_pomodoro_view_timer_input_callback(InputEvent *event, void *ctx) +{ + furi_assert(ctx); + furi_assert(event); + FlippPomodoroTimerView *timer = ctx; + + const bool should_trigger_right_event_cb = (event->type == InputTypePress) && + (event->key == InputKeyRight) && + (timer->right_cb != NULL); + + if (should_trigger_right_event_cb) + { + furi_assert(timer->right_cb); + furi_assert(timer->right_cb_ctx); + timer->right_cb(timer->right_cb_ctx); + return ViewInputConsumed; + }; + + return ViewInputNotConusmed; +}; + +View *flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView *timer) +{ + furi_assert(timer); + return timer->view; +}; + +FlippPomodoroTimerView *flipp_pomodoro_view_timer_alloc() +{ + FlippPomodoroTimerView *timer = malloc(sizeof(FlippPomodoroTimerView)); + timer->view = view_alloc(); + + view_allocate_model(timer->view, ViewModelTypeLockFree, sizeof(FlippPomodoroTimerViewModel)); + view_set_context(flipp_pomodoro_view_timer_get_view(timer), timer); + view_set_draw_callback(timer->view, flipp_pomodoro_view_timer_draw_callback); + view_set_input_callback(timer->view, flipp_pomodoro_view_timer_input_callback); + + return timer; +}; + +void flipp_pomodoro_view_timer_set_on_right_cb(FlippPomodoroTimerView *timer, FlippPomodoroTimerViewInputCb right_cb, void *right_cb_ctx) +{ + furi_assert(right_cb); + furi_assert(right_cb_ctx); + timer->right_cb = right_cb; + timer->right_cb_ctx = right_cb_ctx; +}; + +void flipp_pomodoro_view_timer_set_state(View *view, FlippPomodoroState *state) +{ + furi_assert(view); + furi_assert(state); + with_view_model( + view, + FlippPomodoroTimerViewModel * model, + { + model->state = state; + }, + false); +}; + +void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView *timer) +{ + furi_assert(timer); + view_free(timer->view); + free(timer); +}; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/views/flipp_pomodoro_timer_view.h b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/views/flipp_pomodoro_timer_view.h new file mode 100644 index 000000000..7553d9c27 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipp_pomodoro/views/flipp_pomodoro_timer_view.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "../modules/flipp_pomodoro.h" + +typedef struct FlippPomodoroTimerView FlippPomodoroTimerView; + +typedef void (*FlippPomodoroTimerViewInputCb)(void *context); + +FlippPomodoroTimerView *flipp_pomodoro_view_timer_alloc(); + +View *flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView *timer); + +void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView *timer); + +void flipp_pomodoro_view_timer_set_state(View *view, FlippPomodoroState *state); + +void flipp_pomodoro_view_timer_set_on_right_cb(FlippPomodoroTimerView *timer, FlippPomodoroTimerViewInputCb right_cb, void *right_cb_ctx); diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/LICENSE new file mode 100644 index 000000000..2d8a8a74d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2022-2023 Salvatore Sanfilippo + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/README.md new file mode 100644 index 000000000..7899ff0e2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/README.md @@ -0,0 +1,60 @@ +This is an implementation of the classic Asteroids game for the [Flipper Zero](https://flipperzero.one/). Inside you will find a simple 2D engine that can be reused to implement other games. Note: This one is SimplyMinimal's fork of Antirez's version with several modifications. + +# What's New +* Auto rapid fire (less wear on the buttons this way too) +* Up button applies thrusters +* Haptic feedback and LED effects +* High Score system +* Automatic save and load of high score +* Some modifications to certain game play elements + +## What's coming next +* Settings screen +* Enabling sound effects (configurable on/off option) +* Power Ups + +--- + +This is a screenshot, but the game looks a lot better in the device itself: + +![Asteroids for Flipper Zero screenshot](/images/Asteroids.jpg) + +# Controls: +* Left/Right: rotate ship in the two directions. +* Ok, short press: Short burst bullets +* Ok, long press: Auto-fire bullets +* Up: Accelerate +* Down: Decelerate +* Back (Long Press): Exit game. It will automatically save the high scoore too. + +Your high scores will automatically be saved. Go forth and compete! + +--- +## Installing the binary file (no build needed) + +Go to the releases and drop the `asteroids.fap` file into the +following Flipper Zero location: + + /ext/apps/Games + +The `ext` part means that we are in the SD card. So if you don't want +to use the Android (or other) application to upload the file, +you can just take out the SD card, insert it in your computer, +copy the fine into `apps/Games`, and that's it. + +## Installing the app from source + +* Download the Flipper Zero dev kit and build it: +``` +mkdir -p ~/flipperZero/official/ +cd ~/flipperZero/official/ +git clone --recursive https://github.com/flipperdevices/flipperzero-firmware.git ./ +./fbt +``` +* Copy this application folder in `official/application_user`. +* Connect your Flipper via USB. +* Build and install with: `./fbt launch_app APPSRC=asteroids`. + +## License + +BSD licensed. Enjoy. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/app.c b/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/app.c new file mode 100644 index 000000000..27a44db52 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/app.c @@ -0,0 +1,789 @@ +/* Copyright (C) 2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG "Asteroids" // Used for logging +#define DEBUG_MSG 1 +#define SCREEN_XRES 128 +#define SCREEN_YRES 64 +#define GAME_START_LIVES 3 +#define TTLBUL 30 /* Bullet time to live, in ticks. */ +#define MAXBUL 5 /* Max bullets on the screen. */ +#define MAXAST 32 /* Max asteroids on the screen. */ +#define SHIP_HIT_ANIMATION_LEN 15 +#define SAVING_DIRECTORY "/ext/apps/Games" +#define SAVING_FILENAME SAVING_DIRECTORY "/game_asteroids.save" +#ifndef PI +#define PI 3.14159265358979f +#endif + +/* ============================ Data structures ============================= */ + +typedef struct Ship { + float x, /* Ship x position. */ + y, /* Ship y position. */ + vx, /* x velocity. */ + vy, /* y velocity. */ + rot; /* Current rotation. 2*PI full ortation. */ +} Ship; + +typedef struct Bullet { + float x, y, vx, vy; /* Fields like in ship. */ + uint32_t ttl; /* Time to live, in ticks. */ +} Bullet; + +typedef struct Asteroid { + float x, y, vx, vy, rot, /* Fields like ship. */ + rot_speed, /* Angular velocity (rot speed and sense). */ + size; /* Asteroid size. */ + uint8_t shape_seed; /* Seed to give random shape. */ +} Asteroid; + +typedef struct AsteroidsApp { + /* GUI */ + Gui* gui; + ViewPort* view_port; /* We just use a raw viewport and we render + everything into the low level canvas. */ + FuriMessageQueue* event_queue; /* Keypress events go here. */ + + /* Game state. */ + int running; /* Once false exists the app. */ + bool gameover; /* Gameover status. */ + uint32_t ticks; /* Game ticks. Increments at each refresh. */ + uint32_t score; /* Game score. */ + uint32_t highscore; /* Highscore. Shown on Game Over Screen */ + bool is_new_highscore; /* Is the last score a new highscore? */ + uint32_t lives; /* Number of lives in the current game. */ + uint32_t ship_hit; /* When non zero, the ship was hit by an asteroid + and we need to show an animation as long as + its value is non-zero (and decrease it's value + at each tick of animation). */ + + /* Ship state. */ + struct Ship ship; + + /* Bullets state. */ + struct Bullet bullets[MAXBUL]; /* Each bullet state. */ + int bullets_num; /* Active bullets. */ + uint32_t last_bullet_tick; /* Tick the last bullet was fired. */ + + /* Asteroids state. */ + Asteroid asteroids[MAXAST]; /* Each asteroid state. */ + int asteroids_num; /* Active asteroids. */ + + uint32_t pressed[InputKeyMAX]; /* pressed[id] is true if pressed. + Each array item contains the time + in milliseconds the key was pressed. */ + bool fire; /* Short press detected: fire a bullet. */ +} AsteroidsApp; + +const NotificationSequence sequence_thrusters = { + &message_vibro_on, + &message_delay_10, + &message_vibro_off, + NULL, +}; + +const NotificationSequence sequence_brake = { + &message_vibro_on, + &message_delay_10, + &message_delay_1, + &message_delay_1, + &message_vibro_off, + NULL, +}; + +const NotificationSequence sequence_crash = { + &message_red_255, + + &message_vibro_on, + // &message_note_g5, // Play sound but currently disabled + &message_delay_25, + // &message_note_e5, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_bullet_fired = { + &message_vibro_on, + // &message_note_g5, // Play sound but currently disabled. Need On/Off menu setting + &message_delay_10, + &message_delay_1, + &message_delay_1, + &message_delay_1, + &message_delay_1, + &message_delay_1, + + // &message_note_e5, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +/* ============================== Prototyeps ================================ */ + +// Only functions called before their definition are here. +bool load_game(AsteroidsApp* app); +void save_game(AsteroidsApp* app); +void restart_game_after_gameover(AsteroidsApp* app); +uint32_t key_pressed_time(AsteroidsApp* app, InputKey key); + +/* ============================ 2D drawing ================================== */ + +/* This structure represents a polygon of at most POLY_MAX points. + * The function draw_poly() is able to render it on the screen, rotated + * by the amount specified. */ +#define POLY_MAX 8 +typedef struct Poly { + float x[POLY_MAX]; + float y[POLY_MAX]; + uint32_t points; /* Number of points actually populated. */ +} Poly; + +/* Define the polygons we use. */ +Poly ShipPoly = {{-3, 0, 3}, {-3, 6, -3}, 3}; + +Poly ShipFirePoly = {{-1.5, 0, 1.5}, {-3, -6, -3}, 3}; + +/* Rotate the point of the poligon 'poly' and store the new rotated + * polygon in 'rot'. The polygon is rotated by an angle 'a', with + * center at 0,0. */ +void rotate_poly(Poly* rot, Poly* poly, float a) { + /* We want to compute sin(a) and cos(a) only one time + * for every point to rotate. It's a slow operation. */ + float sin_a = (float)sin(a); + float cos_a = (float)cos(a); + for(uint32_t j = 0; j < poly->points; j++) { + rot->x[j] = poly->x[j] * cos_a - poly->y[j] * sin_a; + rot->y[j] = poly->y[j] * cos_a + poly->x[j] * sin_a; + } + rot->points = poly->points; +} + +/* This is an 8 bit LFSR we use to generate a predictable and fast + * pseudorandom sequence of numbers, to give a different shape to + * each asteroid. */ +void lfsr_next(unsigned char* prev) { + unsigned char lsb = *prev & 1; + *prev = *prev >> 1; + if(lsb == 1) *prev ^= 0b11000111; + *prev ^= *prev << 7; /* Mix things a bit more. */ +} + +/* Render the polygon 'poly' at x,y, rotated by the specified angle. */ +void draw_poly(Canvas* const canvas, Poly* poly, uint8_t x, uint8_t y, float a) { + Poly rot; + rotate_poly(&rot, poly, a); + canvas_set_color(canvas, ColorBlack); + for(uint32_t j = 0; j < rot.points; j++) { + uint32_t a = j; + uint32_t b = j + 1; + if(b == rot.points) b = 0; + canvas_draw_line(canvas, x + rot.x[a], y + rot.y[a], x + rot.x[b], y + rot.y[b]); + } +} + +/* A bullet is just a + pixels pattern. A single pixel is not + * visible enough. */ +void draw_bullet(Canvas* const canvas, Bullet* b) { + canvas_draw_dot(canvas, b->x - 1, b->y); + canvas_draw_dot(canvas, b->x + 1, b->y); + canvas_draw_dot(canvas, b->x, b->y); + canvas_draw_dot(canvas, b->x, b->y - 1); + canvas_draw_dot(canvas, b->x, b->y + 1); +} + +/* Draw an asteroid. The asteroid shapes is computed on the fly and + * is not stored in a permanent shape structure. In order to generate + * the shape, we use an initial fixed shape that we resize according + * to the asteroid size, perturbate according to the asteroid shape + * seed, and finally draw it rotated of the right amount. */ +void draw_asteroid(Canvas* const canvas, Asteroid* ast) { + Poly ap; + + /* Start with what is kinda of a circle. Note that this could be + * stored into a template and copied here, to avoid computing + * sin() / cos(). But the Flipper can handle it without problems. */ + uint8_t r = ast->shape_seed; + for(int j = 0; j < 8; j++) { + float a = (PI * 2) / 8 * j; + + /* Before generating the point, to make the shape unique generate + * a random factor between .7 and 1.3 to scale the distance from + * the center. However this asteroid should have its unique shape + * that remains always the same, so we use a predictable PRNG + * implemented by an 8 bit shift register. */ + lfsr_next(&r); + float scaling = .7 + ((float)r / 255 * .6); + + ap.x[j] = (float)sin(a) * ast->size * scaling; + ap.y[j] = (float)cos(a) * ast->size * scaling; + } + ap.points = 8; + draw_poly(canvas, &ap, ast->x, ast->y, ast->rot); +} + +/* Draw small ships in the top-right part of the screen, one for + * each left live. */ +void draw_left_lives(Canvas* const canvas, AsteroidsApp* app) { + int lives = app->lives; + int x = SCREEN_XRES - 5; + + Poly mini_ship = {{-2, 0, 2}, {-2, 4, -2}, 3}; + while(lives--) { + draw_poly(canvas, &mini_ship, x, 6, PI); + x -= 6; + } +} + +/* Given the current position, update it according to the velocity and + * wrap it back to the other side if the object went over the screen. */ +void update_pos_by_velocity(float* x, float* y, float vx, float vy) { + /* Return back from one side to the other of the screen. */ + *x += vx; + *y += vy; + if(*x >= SCREEN_XRES) + *x = 0; + else if(*x < 0) + *x = SCREEN_XRES - 1; + if(*y >= SCREEN_YRES) + *y = 0; + else if(*y < 0) + *y = SCREEN_YRES - 1; +} + +/* Render the current game screen. */ +void render_callback(Canvas* const canvas, void* ctx) { + AsteroidsApp* app = ctx; + + /* Clear screen. */ + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, 0, SCREEN_XRES - 1, SCREEN_YRES - 1); + + /* Draw score. */ + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + char score[32]; + snprintf(score, sizeof(score), "%lu", app->score); + canvas_draw_str(canvas, 0, 8, score); + + /* Draw left ships. */ + draw_left_lives(canvas, app); + + /* Draw ship, asteroids, bullets. */ + draw_poly(canvas, &ShipPoly, app->ship.x, app->ship.y, app->ship.rot); + + if(key_pressed_time(app, InputKeyUp) > 0) { + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_thrusters); + draw_poly(canvas, &ShipFirePoly, app->ship.x, app->ship.y, app->ship.rot); + } + + for(int j = 0; j < app->bullets_num; j++) draw_bullet(canvas, &app->bullets[j]); + + for(int j = 0; j < app->asteroids_num; j++) draw_asteroid(canvas, &app->asteroids[j]); + + /* Game over text. */ + if(app->gameover) { + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + + // TODO: if new highscore, display blinking "New High Score" + // Display High Score + if(app->is_new_highscore) { + canvas_draw_str(canvas, 22, 9, "New High Score!"); + } else { + canvas_draw_str(canvas, 36, 9, "High Score"); + } + + // Convert highscore to string + int length = snprintf(NULL, 0, "%lu", app->highscore); + char* str_high_score = malloc(length + 1); + snprintf(str_high_score, length + 1, "%lu", app->highscore); + + // Get length to center on screen + int nDigits = 0; + if(app->highscore > 0) { + nDigits = floor(log10(app->highscore)) + 1; + } + + // Draw highscore centered + canvas_draw_str(canvas, (SCREEN_XRES / 2) - (nDigits * 2), 20, str_high_score); + free(str_high_score); + + canvas_draw_str(canvas, 28, 35, "GAME OVER"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 25, 50, "Press OK to restart"); + } +} + +/* ============================ Game logic ================================== */ + +float distance(float x1, float y1, float x2, float y2) { + float dx = x1 - x2; + float dy = y1 - y2; + return sqrt(dx * dx + dy * dy); +} + +/* Detect a collision between the object at x1,y1 of radius r1 and + * the object at x2, y2 of radius r2. A factor < 1 will make the + * function detect the collision even if the objects are yet not + * relly touching, while a factor > 1 will make it detect the collision + * only after they are a bit overlapping. It basically is used to + * rescale the distance. + * + * Note that in this simplified 2D world, objects are all considered + * spheres (this is why this function only takes the radius). This + * is, after all, kinda accurate for asteroids, for bullets, and + * even for the ship "core" itself. */ +bool objects_are_colliding(float x1, float y1, float r1, float x2, float y2, float r2, float factor) { + /* The objects are colliding if the distance between object 1 and 2 + * is smaller than the sum of the two radiuses r1 and r2. + * So it would be like: sqrt((x1-x2)^2+(y1-y2)^2) < r1+r2. + * However we can avoid computing the sqrt (which is slow) by + * squaring the second term and removing the square root, making + * the comparison like this: + * + * (x1-x2)^2+(y1-y2)^2 < (r1+r2)^2. */ + float dx = (x1 - x2) * factor; + float dy = (y1 - y2) * factor; + float rsum = r1 + r2; + return dx * dx + dy * dy < rsum * rsum; +} + +/* Create a new bullet headed in the same direction of the ship. */ +void ship_fire_bullet(AsteroidsApp* app) { + if(app->bullets_num == MAXBUL) return; + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_bullet_fired); + Bullet* b = &app->bullets[app->bullets_num]; + b->x = app->ship.x; + b->y = app->ship.y; + b->vx = -sin(app->ship.rot); + b->vy = cos(app->ship.rot); + + /* Ship should fire from its head, not in the middle. */ + b->x += b->vx * 5; + b->y += b->vy * 5; + + /* Give the bullet some velocity (for now the vector is just + * normalized to 1). */ + b->vx *= 3; + b->vy *= 3; + + /* It's more realistic if we add the velocity vector of the + * ship, too. Otherwise if the ship is going fast the bullets + * will be slower, which is not how the world works. */ + b->vx += app->ship.vx; + b->vy += app->ship.vy; + + b->ttl = TTLBUL; /* The bullet will disappear after N ticks. */ + app->bullets_num++; +} + +/* Remove the specified bullet by id (index in the array). */ +void remove_bullet(AsteroidsApp* app, int bid) { + /* Replace the top bullet with the empty space left + * by the removal of this bullet. This way we always take the + * array dense, which is an advantage when looping. */ + int n = --app->bullets_num; + if(n && bid != n) app->bullets[bid] = app->bullets[n]; +} + +/* Create a new asteroid, away from the ship. Return the + * pointer to the asteroid object, so that the caller can change + * certain things of the asteroid if needed. */ +Asteroid* add_asteroid(AsteroidsApp* app) { + if(app->asteroids_num == MAXAST) return NULL; + float size = 4 + rand() % 15; + float min_distance = 20; + float x, y; + do { + x = rand() % SCREEN_XRES; + y = rand() % SCREEN_YRES; + } while(distance(app->ship.x, app->ship.y, x, y) < min_distance + size); + Asteroid* a = &app->asteroids[app->asteroids_num++]; + a->x = x; + a->y = y; + a->vx = 2 * (-.5 + ((float)rand() / RAND_MAX)); + a->vy = 2 * (-.5 + ((float)rand() / RAND_MAX)); + a->size = size; + a->rot = 0; + a->rot_speed = ((float)rand() / RAND_MAX) / 10; + if(app->ticks & 1) a->rot_speed = -(a->rot_speed); + a->shape_seed = rand() & 255; + return a; +} + +/* Remove the specified asteroid by id (index in the array). */ +void remove_asteroid(AsteroidsApp* app, int id) { + /* Replace the top asteroid with the empty space left + * by the removal of this one. This way we always take the + * array dense, which is an advantage when looping. */ + int n = --app->asteroids_num; + if(n && id != n) app->asteroids[id] = app->asteroids[n]; +} + +/* Called when an asteroid was reached by a bullet. The asteroid + * hit is the one with the specified 'id'. */ +void asteroid_was_hit(AsteroidsApp* app, int id) { + float sizelimit = 6; // Smaller than that polverize in one shot. + Asteroid* a = &app->asteroids[id]; + + /* Asteroid is large enough to break into fragments. */ + float size = a->size; + float x = a->x, y = a->y; + remove_asteroid(app, id); + if(size > sizelimit) { + int max_fragments = size / sizelimit; + int fragments = 2 + rand() % max_fragments; + float newsize = size / fragments; + if(newsize < 2) newsize = 2; + for(int j = 0; j < fragments; j++) { + a = add_asteroid(app); + if(a == NULL) break; // Too many asteroids on screen. + a->x = x + -(size / 2) + rand() % (int)newsize; + a->y = y + -(size / 2) + rand() % (int)newsize; + a->size = newsize; + } + } else { + app->score++; + if(app->score > app->highscore) { + app->is_new_highscore = true; + app->highscore = app->score; // Show on Game Over Screen and future main menu + } + } +} + +/* Set gameover state. When in game-over mode, the game displays a gameover + * text with a background of many asteroids floating around. */ +void game_over(AsteroidsApp* app) { + if(app->is_new_highscore) save_game(app); // Save highscore but only on change + app->gameover = true; + app->lives = GAME_START_LIVES; // Show 3 lives in game over screen to match new game start +} + +/* Function called when a collision between the asteroid and the + * ship is detected. */ +void ship_was_hit(AsteroidsApp* app) { + app->ship_hit = SHIP_HIT_ANIMATION_LEN; + if(app->lives) { + app->lives--; + } else { + game_over(app); + } +} + +/* Restart game after the ship is hit. Will reset the ship position, bullets + * and asteroids to restart the game. */ +void restart_game(AsteroidsApp* app) { + app->ship.x = SCREEN_XRES / 2; + app->ship.y = SCREEN_YRES / 2; + app->ship.rot = PI; /* Start headed towards top. */ + app->ship.vx = 0; + app->ship.vy = 0; + app->bullets_num = 0; + app->last_bullet_tick = 0; + app->asteroids_num = 0; + app->ship_hit = 0; +} + +/* Called after gameover to restart the game. This function + * also calls restart_game(). */ +void restart_game_after_gameover(AsteroidsApp* app) { + app->gameover = false; + app->ticks = 0; + app->score = 0; + app->is_new_highscore = false; + app->lives = GAME_START_LIVES - 1; + restart_game(app); +} + +/* Move bullets. */ +void update_bullets_position(AsteroidsApp* app) { + for(int j = 0; j < app->bullets_num; j++) { + update_pos_by_velocity( + &app->bullets[j].x, &app->bullets[j].y, app->bullets[j].vx, app->bullets[j].vy); + if(--app->bullets[j].ttl == 0) { + remove_bullet(app, j); + j--; /* Process this bullet index again: the removal will + fill it with the top bullet to take the array dense. */ + } + } +} + +/* Move asteroids. */ +void update_asteroids_position(AsteroidsApp* app) { + for(int j = 0; j < app->asteroids_num; j++) { + update_pos_by_velocity( + &app->asteroids[j].x, &app->asteroids[j].y, app->asteroids[j].vx, app->asteroids[j].vy); + app->asteroids[j].rot += app->asteroids[j].rot_speed; + if(app->asteroids[j].rot < 0) + app->asteroids[j].rot = 2 * PI; + else if(app->asteroids[j].rot > 2 * PI) + app->asteroids[j].rot = 0; + } +} + +/* Collision detection and game state update based on collisions. */ +void detect_collisions(AsteroidsApp* app) { + /* Detect collision between bullet and asteroid. */ + for(int j = 0; j < app->bullets_num; j++) { + Bullet* b = &app->bullets[j]; + for(int i = 0; i < app->asteroids_num; i++) { + Asteroid* a = &app->asteroids[i]; + if(objects_are_colliding(a->x, a->y, a->size, b->x, b->y, 1.5, 1)) { + asteroid_was_hit(app, i); + remove_bullet(app, j); + /* The bullet no longer exist. Break the loop. + * However we want to start processing from the + * same bullet index, since now it is used by + * another bullet (see remove_bullet()). */ + j--; /* Scan this j value again. */ + break; + } + } + } + + /* Detect collision between ship and asteroid. */ + for(int j = 0; j < app->asteroids_num; j++) { + Asteroid* a = &app->asteroids[j]; + if(objects_are_colliding(a->x, a->y, a->size, app->ship.x, app->ship.y, 4, 1)) { + ship_was_hit(app); + break; + } + } +} + +/* This is the main game execution function, called 10 times for + * second (with the Flipper screen latency, an higher FPS does not + * make sense). In this function we update the position of objects based + * on velocity. Detect collisions. Update the score and so forth. + * + * Each time this function is called, app->tick is incremented. */ +void game_tick(void* ctx) { + AsteroidsApp* app = ctx; + + /* There are two special screens: + * + * 1. Ship was hit, we frozen the game as long as ship_hit isn't zero + * again, and show an animation of a rotating ship. */ + if(app->ship_hit) { + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_crash); + app->ship.rot += 0.5; + app->ship_hit--; + view_port_update(app->view_port); + if(app->ship_hit == 0) { + restart_game(app); + } + return; + } else if(app->gameover) { + /* 2. Game over. We need to update only background asteroids. In this + * state the game just displays a GAME OVER text with the floating + * asteroids in backgroud. */ + + if(key_pressed_time(app, InputKeyOk) > 100) { + restart_game_after_gameover(app); + } + update_asteroids_position(app); + view_port_update(app->view_port); + return; + } + + /* Handle keypresses. */ + if(app->pressed[InputKeyLeft]) app->ship.rot -= .35; + if(app->pressed[InputKeyRight]) app->ship.rot += .35; + if(app->pressed[InputKeyUp]) { + app->ship.vx -= 0.5 * (float)sin(app->ship.rot); + app->ship.vy += 0.5 * (float)cos(app->ship.rot); + } else if(app->pressed[InputKeyDown]) { + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_brake); + app->ship.vx *= 0.75; + app->ship.vy *= 0.75; + } + + /* Fire a bullet if needed. app->fire is set in + * asteroids_update_keypress_state() since depends on exact + * pressure timing. */ + if(app->fire) { + uint32_t bullet_min_period = 200; // In milliseconds + uint32_t now = furi_get_tick(); + if(now - app->last_bullet_tick >= bullet_min_period) { + ship_fire_bullet(app); + app->last_bullet_tick = now; + } + app->fire = false; + } + + /* Update positions and detect collisions. */ + update_pos_by_velocity(&app->ship.x, &app->ship.y, app->ship.vx, app->ship.vy); + update_bullets_position(app); + update_asteroids_position(app); + detect_collisions(app); + + /* From time to time, create a new asteroid. The more asteroids + * already on the screen, the smaller probability of creating + * a new one. */ + if(app->asteroids_num == 0 || (random() % 5000) < (30 / (1 + app->asteroids_num))) { + add_asteroid(app); + } + + app->ticks++; + view_port_update(app->view_port); +} + +/* ======================== Flipper specific code =========================== */ + +bool load_game(AsteroidsApp* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + File* file = storage_file_alloc(storage); + uint16_t bytes_readed = 0; + if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) { + bytes_readed = storage_file_read(file, app, sizeof(AsteroidsApp)); + } + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); + + return bytes_readed == sizeof(AsteroidsApp); +} + +void save_game(AsteroidsApp* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { + if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { + return; + } + } + + File* file = storage_file_alloc(storage); + if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, app, sizeof(AsteroidsApp)); + } + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); +} + +/* Here all we do is putting the events into the queue that will be handled + * in the while() loop of the app entry point function. */ +void input_callback(InputEvent* input_event, void* ctx) { + AsteroidsApp* app = ctx; + furi_message_queue_put(app->event_queue, input_event, FuriWaitForever); +} + +/* Allocate the application state and initialize a number of stuff. + * This is called in the entry point to create the application state. */ +AsteroidsApp* asteroids_app_alloc() { + AsteroidsApp* app = malloc(sizeof(AsteroidsApp)); + + load_game(app); + + app->gui = furi_record_open(RECORD_GUI); + app->view_port = view_port_alloc(); + view_port_draw_callback_set(app->view_port, render_callback, app); + view_port_input_callback_set(app->view_port, input_callback, app); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + app->running = 1; /* Turns 0 when back is pressed. */ + + restart_game_after_gameover(app); + memset(app->pressed, 0, sizeof(app->pressed)); + return app; +} + +/* Free what the application allocated. It is not clear to me if the + * Flipper OS, once the application exits, will be able to reclaim space + * even if we forget to free something here. */ +void asteroids_app_free(AsteroidsApp* app) { + furi_assert(app); + + // View related. + view_port_enabled_set(app->view_port, false); + gui_remove_view_port(app->gui, app->view_port); + view_port_free(app->view_port); + furi_record_close(RECORD_GUI); + furi_message_queue_free(app->event_queue); + app->gui = NULL; + + free(app); +} + +/* Return the time in milliseconds the specified key is continuously + * pressed. Or 0 if it is not pressed. */ +uint32_t key_pressed_time(AsteroidsApp* app, InputKey key) { + return app->pressed[key] == 0 ? 0 : furi_get_tick() - app->pressed[key]; +} + +/* Handle keys interaction. */ +void asteroids_update_keypress_state(AsteroidsApp* app, InputEvent input) { + // Allow Rapid fire + if(input.key == InputKeyOk) { + app->fire = true; + } + + if(input.type == InputTypePress) { + app->pressed[input.key] = furi_get_tick(); + } else if(input.type == InputTypeRelease) { + app->pressed[input.key] = 0; + } +} + +int32_t asteroids_app_entry(void* p) { + UNUSED(p); + AsteroidsApp* app = asteroids_app_alloc(); + + /* Create a timer. We do data analysis in the callback. */ + FuriTimer* timer = furi_timer_alloc(game_tick, FuriTimerTypePeriodic, app); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); + + /* This is the main event loop: here we get the events that are pushed + * in the queue by input_callback(), and process them one after the + * other. */ + InputEvent input; + while(app->running) { + FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100); + if(qstat == FuriStatusOk) { + if(DEBUG_MSG) + FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key); + + /* Handle navigation here. Then handle view-specific inputs + * in the view specific handling function. */ + if(input.type == InputTypeLong && input.key == InputKeyBack) { + // Save High Score even if player didn't finish game + if(app->is_new_highscore) save_game(app); // Save highscore but only on change + app->running = 0; + } else { + asteroids_update_keypress_state(app, input); + } + } else { + /* Useful to understand if the app is still alive when it + * does not respond because of bugs. */ + if(DEBUG_MSG) { + static int c = 0; + c++; + if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout"); + } + } + } + + furi_timer_free(timer); + asteroids_app_free(app); + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/appicon.png b/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/appicon.png new file mode 100644 index 000000000..45da095af Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/appicon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/application.fam new file mode 100644 index 000000000..c1f7ed2bc --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/application.fam @@ -0,0 +1,12 @@ +App( + appid="asteroids", + name="Asteroids", + apptype=FlipperAppType.EXTERNAL, + entry_point="asteroids_app_entry", + cdefines=["APP_ASTEROIDS"], + requires=["gui"], + stack_size=8*1024, + order=50, + fap_icon="appicon.png", + fap_category="Games_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/images/Asteroids.jpg b/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/images/Asteroids.jpg new file mode 100644 index 000000000..1e2e43ca8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper-asteroids/images/Asteroids.jpg differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/README.md new file mode 100644 index 000000000..75c27eadb --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/README.md @@ -0,0 +1,3 @@ +# Flipper BP + +Custom implementation of T-Code protocol for Flipper Zero devices. \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/application.fam new file mode 100644 index 000000000..bddfbf022 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/application.fam @@ -0,0 +1,13 @@ +App( + appid="fbp", + name="Flipper BP", + apptype=FlipperAppType.EXTERNAL, + entry_point="fbp_app", + stack_size=1 * 1024, + requires=[ + "bt", + "gui", + ], + fap_category="Misc_Extra", + fap_icon="uart_10px.png", +) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/fbp.c b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/fbp.c new file mode 100644 index 000000000..60083a4a6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/fbp.c @@ -0,0 +1,103 @@ +#include "fbp.h" + +enum FBPSubmenuIndex { + FBPSubmenuIndexInternal, + FBPSubmenuIndexGPIOSimpleMotor, +}; + +uint32_t fbp_start_view(void* context) { + UNUSED(context); + return FBPAppViewSubmenu; +} + +uint32_t fbp_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +void fbp_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + FBP* app = context; + if(index == FBPSubmenuIndexInternal) { + view_dispatcher_switch_to_view(app->view_dispatcher, FBPAppViewInternal); + } else if (index == FBPSubmenuIndexGPIOSimpleMotor) { + view_dispatcher_switch_to_view(app->view_dispatcher, FBPAppViewGPIOSimpleMotor); + } +} + +FBP* fbp_alloc() { + FBP* app = malloc(sizeof(FBP)); + app->app_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + app->bt_connected = false; + app->bt = furi_record_open(RECORD_BT); + + app->gui = furi_record_open(RECORD_GUI); + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // set submenu + app->submenu = submenu_alloc(); + view_set_previous_callback(submenu_get_view(app->submenu), fbp_exit); + view_dispatcher_add_view(app->view_dispatcher, FBPAppViewSubmenu, submenu_get_view(app->submenu)); + + // add Flipper Internal View + app->flipper_vibrator = flipper_vibrator_alloc(app); + submenu_add_item(app->submenu, "Flipper Internal Vibrator", FBPSubmenuIndexInternal, fbp_submenu_callback, app); + view_set_previous_callback(flipper_vibrator_get_view(app->flipper_vibrator), fbp_start_view); + view_dispatcher_add_view(app->view_dispatcher, FBPAppViewInternal, flipper_vibrator_get_view(app->flipper_vibrator)); + + // add GPIO Simple Motor View + app->gpio_simple_motor = gpio_simple_motor_alloc(app); + submenu_add_item(app->submenu, "Flipper GPIO Simple Motor", FBPSubmenuIndexGPIOSimpleMotor, fbp_submenu_callback, app); + view_set_previous_callback(gpio_simple_motor_get_view(app->gpio_simple_motor), fbp_start_view); + view_dispatcher_add_view(app->view_dispatcher, FBPAppViewGPIOSimpleMotor, gpio_simple_motor_get_view(app->gpio_simple_motor)); + + view_dispatcher_switch_to_view(app->view_dispatcher, FBPAppViewSubmenu); + return app; +} + +void fbs_free(FBP* app) { + furi_assert(app); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, FBPAppViewSubmenu); + submenu_free(app->submenu); + + // free Flipper Internal Vibrator + view_dispatcher_remove_view(app->view_dispatcher, FBPAppViewInternal); + flipper_vibrator_free(app->flipper_vibrator); + + // free GPIO Simple Motor + view_dispatcher_remove_view(app->view_dispatcher, FBPAppViewGPIOSimpleMotor); + gpio_simple_motor_free(app->gpio_simple_motor); + + // Other deallocations + view_dispatcher_free(app->view_dispatcher); + + + furi_record_close(RECORD_GUI); + app->gui = NULL; + + furi_mutex_free(app->app_mutex); + furi_message_queue_free(app->event_queue); + + furi_record_close(RECORD_BT); + app->bt = NULL; + + free(app); +} + +int32_t fbp_app(void* p) { + UNUSED(p); + FBP* app = fbp_alloc(); + + view_dispatcher_run(app->view_dispatcher); + + furi_hal_bt_serial_set_event_callback(0, NULL, NULL); + + fbs_free(app); + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/fbp.h b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/fbp.h new file mode 100644 index 000000000..bd669ad03 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/fbp.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "tcode.h" +#include "views/internal.h" +#include "views/gpio_simple_motor.h" + +#define TAG "Flipper BP" + +typedef struct FBP FBP; + +struct FBP { + Submenu* submenu; + ViewDispatcher* view_dispatcher; + Gui* gui; + + Bt* bt; + bool bt_connected; + + FuriMutex* app_mutex; + FuriMessageQueue* event_queue; + + FlipperVibrator* flipper_vibrator; + GPIOSimpleMotor* gpio_simple_motor; +}; + +typedef enum { + FBPAppViewSubmenu, + FBPAppViewInternal, + FBPAppViewGPIOSimpleMotor, +} FBPAppView; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/tcode.c b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/tcode.c new file mode 100644 index 000000000..d0699e201 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/tcode.c @@ -0,0 +1,183 @@ +#include "tcode.h" + +static TCodeCommandArray decode_device_command(const uint8_t *buffer, uint16_t size) { + TCodeCommandArray command_array; + if (size < 2) { + command_array.size = 0; + FURI_LOG_W("TCode Parser", "Unexpected code length for device command"); + return command_array; + } + + command_array.size = 1; + command_array.commands = malloc(sizeof(TCodeCommand)); + command_array.commands[0].command_type = Device; + + switch (buffer[1]) { + case '0': + FURI_LOG_T("TCode Parser", "Device Identification requested"); + command_array.commands[0].data.device_command = DeviceIdentification; + break; + case '1': + FURI_LOG_T("TCode Parser", "TCode version requested"); + command_array.commands[0].data.device_command = TCodeVersion; + break; + case '2': + FURI_LOG_T("TCode Parser", "Preferences list requested"); + command_array.commands[0].data.device_command = ListAxesAndUserRangePreferences; + break; + case 'S': + FURI_LOG_T("TCode Parser", "Stop requested"); + command_array.commands[0].data.device_command = Stop; + break; + default: + command_array.size = 0; + break; + } + + return command_array; +} + +static TCodeCommandArray decode_general_command(const uint8_t *buffer, uint16_t size) { + // size of the array = amount of spaces + 1 + uint16_t counter = 1; + for (uint16_t i = 0; i < size; i++) { + if (buffer[i] == 32) { + counter++; + } + } + + FURI_LOG_T("TCode Parser", "Found %u commands in the message", counter); + + TCodeCommandArray commands_array; + commands_array.size = counter; + commands_array.commands = malloc(commands_array.size * sizeof(TCodeCommand)); + + uint16_t position = 0; + for (uint16_t i = 0; i < counter; i++) { + TCodeCommand command; + command.command_type = Unknown; + + TCodeCommandMotionType motion_type; + switch (buffer[position]) { + case 'L': + case 'l': + motion_type = Linear; + break; + case 'R': + case 'r': + motion_type = Rotate; + break; + case 'V': + case 'v': + motion_type = Vibrate; + break; + case 'A': + case 'a': + motion_type = Auxiliary; + break; + default: // error + FURI_LOG_W("TCode Parser", "Unexpected motion type: %u", buffer[position]); + return commands_array; + } + FURI_LOG_T("TCode Parser", "Parsed motion_type: %u", motion_type); + position++; + + uint16_t channel = buffer[position] - 48; // single ascii character 0-9 + FURI_LOG_T("TCode Parser", "Parsed channel: %u", channel); + position++; + + // X characters that are digits + uint16_t current_position = position; + while (buffer[position] >= 48 && buffer[position] <= 57 && position < size) { + position++; + } + + uint8_t *magnitude = malloc(2 + (position - current_position) + 1); // "0.XXXX\0" + magnitude[0] = '0'; + magnitude[1] = '.'; + for (uint16_t x = 0; x < (position - current_position); x++) { + magnitude[x + 2] = buffer[current_position + x]; + } + magnitude[position - current_position + 2] = '\0'; + float magnitude_float = strtof((char *) magnitude, NULL); + free(magnitude); + FURI_LOG_T("TCode Parser", "Parsed magnitude: %f", (double) magnitude_float); + + FURI_LOG_T("TCode Parser REMOVE ME", "Current position: %u, size: %u", position, size); + FURI_LOG_T("TCode Parser REMOVE ME", "%u", buffer[position]); + if (position == size || buffer[position] == ' ' || buffer[position] == '\n') { + FURI_LOG_T("TCode Parser", "Command type: Magnitude"); + command.command_type = Magnitude; + command.data.magnitude_command.motion_type = motion_type; + command.data.magnitude_command.channel_id = channel; + command.data.magnitude_command.magnitude = magnitude_float; + commands_array.commands[i] = command; + position++; + continue; + } + + uint8_t current_step = buffer[position]; + position++; + + uint16_t int_value = 0; + while (buffer[position] >= 48 && buffer[position] <= 57 && position < size) { + int_value *= 10; + int_value += buffer[position] - 48; + position++; + } + + command.data.magnitude_time_interval_command.motion_type = motion_type; + command.data.magnitude_time_interval_command.channel_id = channel; + command.data.magnitude_time_interval_command.magnitude = magnitude_float; + + if (current_step == 'I' || current_step == 'i') { + FURI_LOG_T("TCode Parser", "Command type: MagnitudeTimeInterval"); + command.command_type = MagnitudeTimeInterval; + command.data.magnitude_time_interval_command.time_interval_milliseconds = int_value; + } + + if (current_step == 'S' || current_step == 's') { + FURI_LOG_T("TCode Parser", "Command type: MagnitudeSpeed"); + command.command_type = MagnitudeSpeed; + command.data.magnitude_speed_command.speed_per_hundred_milliseconds = int_value; + } + + if (command.command_type == Unknown) { + FURI_LOG_W("TCode Parser", "Unknown command type!"); + } + + commands_array.commands[i] = command; + position++; + if (position >= size) { + break; + } + } + + return commands_array; +} + +TCodeCommandArray tcode_decode(uint8_t *buffer, uint16_t size) { + switch (buffer[0]) { + case 'd': + case 'D': + FURI_LOG_T("TCode Parser", "Parsing device command..."); + return decode_device_command(buffer, size); + case 'l': + case 'L': + case 'r': + case 'R': + case 'v': + case 'V': + case 'a': + case 'A': + FURI_LOG_T("TCode Parser", "Parsing general command..."); + return decode_general_command(buffer, size); + default: // error + { + TCodeCommandArray error; + error.size = 0; + error.commands = NULL; + return error; + } + } +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/tcode.h b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/tcode.h new file mode 100644 index 000000000..7174965fe --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/tcode.h @@ -0,0 +1,63 @@ +#pragma once +#include + +typedef enum { + Device, + Magnitude, + MagnitudeTimeInterval, + MagnitudeSpeed, + Unknown, +} TCodeCommandType; + +typedef enum { + DeviceIdentification, + TCodeVersion, + ListAxesAndUserRangePreferences, + Stop, +} DeviceCommand; + +typedef enum { + Linear, + Rotate, + Vibrate, + Auxiliary, +} TCodeCommandMotionType; + +typedef struct { + TCodeCommandMotionType motion_type; + uint8_t channel_id; + float magnitude; +} TCodeMagnitudeCommand; + +typedef struct { + TCodeCommandMotionType motion_type; + uint8_t channel_id; + float magnitude; + uint16_t time_interval_milliseconds; +} TCodeMagnitudeTimeIntervalCommand; + +typedef struct { + TCodeCommandMotionType motion_type; + uint8_t channel_id; + float magnitude; + uint16_t speed_per_hundred_milliseconds; +} TCodeMagnitudeSpeedCommand; + + +typedef struct { + TCodeCommandType command_type; + union { + DeviceCommand device_command; + TCodeMagnitudeCommand magnitude_command; + TCodeMagnitudeTimeIntervalCommand magnitude_time_interval_command; + TCodeMagnitudeSpeedCommand magnitude_speed_command; + } data; +} TCodeCommand; + +typedef struct { + uint16_t size; + TCodeCommand *commands; +} TCodeCommandArray; + + +TCodeCommandArray tcode_decode(uint8_t *buffer, uint16_t size); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/uart_10px.png b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/uart_10px.png new file mode 100644 index 000000000..8420f5692 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/uart_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/views/gpio_simple_motor.c b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/views/gpio_simple_motor.c new file mode 100644 index 000000000..5fef098dc --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/views/gpio_simple_motor.c @@ -0,0 +1,149 @@ +#include "gpio_simple_motor.h" +#include "../fbp.h" + +static const uint16_t BT_SERIAL_BUFFER_SIZE = 128; +static const uint32_t DEFAULT_FREQ = 1000; +static const FuriHalPwmOutputId DEFAULT_PWM_OUTPUT_ID = FuriHalPwmOutputIdTim1PA7; + +struct GPIOSimpleMotor { + View* view; + FBP* fbp; + + uint8_t current_pwm_duty; +}; + +typedef struct { + char* display_text_1; + char* display_text_2; + char* display_text_3; +} GPIOSimpleMotorModel; + + +static void process_general_command(TCodeCommand command, GPIOSimpleMotor* motor) { + if (command.command_type == Magnitude && command.data.magnitude_command.motion_type == Vibrate && command.data.magnitude_command.channel_id == 0) { + // just enable vibration on X + uint8_t new_duty = (uint8_t) (command.data.magnitude_command.magnitude * 100); + if (new_duty > 100) { + new_duty = 100; + } + FURI_LOG_D(TAG, "Setting vibration power on %u", new_duty); + + // using Pulse-Widht Modulation to control a motor via a transistor + // just google for a typical arduino + PWM + motor scheme + if (new_duty == 0) { + furi_hal_pwm_stop(DEFAULT_PWM_OUTPUT_ID); + } else if (motor->current_pwm_duty == 0) { + furi_hal_pwm_start(DEFAULT_PWM_OUTPUT_ID, DEFAULT_FREQ, new_duty); + } else { + furi_hal_pwm_set_params(DEFAULT_PWM_OUTPUT_ID, DEFAULT_FREQ, new_duty); + } + motor->current_pwm_duty = new_duty; + return; + } +} + +static uint16_t bt_serial_event_callback(SerialServiceEvent event, void* context) { + furi_assert(context); + GPIOSimpleMotor* motor = context; + + if(event.event == SerialServiceEventTypeDataReceived) { + TCodeCommandArray commands = tcode_decode(event.data.buffer, event.data.size); + FURI_LOG_D(TAG, "Decoded commands array size: %u", commands.size); + for (uint16_t i = 0; i < commands.size; i++) { + FURI_LOG_D(TAG, "Command #%u, type: %u\n", i, commands.commands[i].command_type); + } + for (uint16_t i = 0; i < commands.size; i++) { + // looking for first vibro command to execute + TCodeCommand current_command = commands.commands[i]; + TCodeCommandType type = current_command.command_type; + if ((type == Magnitude || type == MagnitudeSpeed || type == MagnitudeTimeInterval)) { + process_general_command(current_command, motor); + } + } + } + return 0; +} + +static bool input_callback(InputEvent* event, void* ctx) { + furi_assert(ctx); + GPIOSimpleMotor* motor = ctx; + if(event->key == InputKeyBack) { + furi_hal_bt_serial_set_event_callback(0, NULL, NULL); + return false; + } + + if (event->key == InputKeyOk) { + if (furi_hal_bt_is_active()) { + FURI_LOG_D(TAG, "BT is working, hijacking the serial connection..."); + furi_hal_bt_start_advertising(); + furi_hal_bt_serial_set_event_callback(BT_SERIAL_BUFFER_SIZE, bt_serial_event_callback, motor); + + with_view_model( + motor->view, + GPIOSimpleMotorModel * model, + { + model->display_text_1 = ""; + model->display_text_2 = "Ready ^_^"; + model->display_text_3 = ""; + }, + true); + + } else { + FURI_LOG_E(TAG, "Please, enable the Bluetooth and restart the app"); + + with_view_model( + motor->view, + GPIOSimpleMotorModel * model, + { + model->display_text_1 = "Error:"; + model->display_text_2 = "Bluetooth is not enabled"; + model->display_text_3 = ""; + }, + true); + } + } + return true; +} + +static void draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + GPIOSimpleMotorModel* app = ctx; + canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignCenter, (char*)app->display_text_1); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, (char*)app->display_text_2); + canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, (char*)app->display_text_3); +} + +GPIOSimpleMotor* gpio_simple_motor_alloc(FBP* fbp) { + furi_assert(fbp); + GPIOSimpleMotor* motor = malloc(sizeof(GPIOSimpleMotor)); + motor->view = view_alloc(); + motor->fbp = fbp; + view_set_context(motor->view, motor); + view_allocate_model(motor->view, ViewModelTypeLocking, sizeof(GPIOSimpleMotorModel)); + view_set_draw_callback(motor->view, draw_callback); + view_set_input_callback(motor->view, input_callback); + + with_view_model( + motor->view, + GPIOSimpleMotorModel * model, + { + model->display_text_1 = "Please, connect the"; + model->display_text_2 = "transistor base to pin A7!"; + model->display_text_3 = "Press OK to start"; + }, + true); + + return motor; +} + +void gpio_simple_motor_free(GPIOSimpleMotor* motor) { + furi_assert(motor); + furi_hal_pwm_stop(DEFAULT_PWM_OUTPUT_ID); + view_free(motor->view); + free(motor); +} + +View* gpio_simple_motor_get_view(GPIOSimpleMotor* motor) { + furi_assert(motor); + return motor->view; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/views/gpio_simple_motor.h b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/views/gpio_simple_motor.h new file mode 100644 index 000000000..e2668a45d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/views/gpio_simple_motor.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include +#include + +typedef struct FBP FBP; +typedef struct GPIOSimpleMotor GPIOSimpleMotor; + +GPIOSimpleMotor* gpio_simple_motor_alloc(FBP* fbp); + +void gpio_simple_motor_free(GPIOSimpleMotor* motor_app); + +View* gpio_simple_motor_get_view(GPIOSimpleMotor* motor_app); diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/views/internal.c b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/views/internal.c new file mode 100644 index 000000000..8d458d920 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/views/internal.c @@ -0,0 +1,133 @@ +#include "internal.h" +#include "../fbp.h" + +static const uint16_t BT_SERIAL_BUFFER_SIZE = 128; + +struct FlipperVibrator { + View* view; + FBP* fbp; +}; + +typedef struct { + char* display_text; +} FlipperVibratorModel; + + +static void process_general_command(TCodeCommand command) { + if (command.command_type == Magnitude && command.data.magnitude_command.motion_type == Vibrate && command.data.magnitude_command.channel_id == 0) { + furi_hal_vibro_on(command.data.magnitude_command.magnitude > 0.1f); + return; + } + + if (command.command_type == MagnitudeSpeed && command.data.magnitude_speed_command.motion_type == Vibrate && command.data.magnitude_speed_command.channel_id == 0) { + furi_hal_vibro_on(command.data.magnitude_speed_command.magnitude > 0.1f); + return; + } + + if (command.command_type == MagnitudeTimeInterval && command.data.magnitude_time_interval_command.motion_type == Vibrate && command.data.magnitude_time_interval_command.channel_id == 0) { + furi_hal_vibro_on(command.data.magnitude_time_interval_command.magnitude > 0.1f); + return; + } +} + + +static uint16_t bt_serial_event_callback(SerialServiceEvent event, void* context) { + furi_assert(context); + FlipperVibrator* flipper_vibrator = context; + UNUSED(flipper_vibrator); + + if(event.event == SerialServiceEventTypeDataReceived) { + FURI_LOG_D(TAG, "SerialServiceEventTypeDataReceived"); + FURI_LOG_D(TAG, "Size: %u", event.data.size); + FURI_LOG_D(TAG, "Data: "); + for (size_t i = 0; i < event.data.size; i++) + { + printf("%X ", event.data.buffer[i]); + } + printf("\r\n"); + + TCodeCommandArray commands = tcode_decode(event.data.buffer, event.data.size); + FURI_LOG_D(TAG, "Decoded commands array size: %u", commands.size); + for (uint16_t i = 0; i < commands.size; i++) { + FURI_LOG_D(TAG, "Command #%u, type: %u\n", i, commands.commands[i].command_type); + } + for (uint16_t i = 0; i < commands.size; i++) { + // looking for first vibro command to execute + TCodeCommand current_command = commands.commands[i]; + TCodeCommandType type = current_command.command_type; + if ((type == Magnitude || type == MagnitudeSpeed || type == MagnitudeTimeInterval)) { + process_general_command(current_command); + } + } + } + return 0; +} + +static bool input_callback(InputEvent* event, void* ctx) { + furi_assert(ctx); + FlipperVibrator* flipper_vibrator = ctx; + if(event->key == InputKeyBack) { + furi_hal_bt_serial_set_event_callback(0, NULL, NULL); + return false; + } + + if (event->key == InputKeyOk) { + if (furi_hal_bt_is_active()) { + FURI_LOG_D(TAG, "BT is working, hijacking the serial connection..."); + furi_hal_bt_start_advertising(); + furi_hal_bt_serial_set_event_callback(BT_SERIAL_BUFFER_SIZE, bt_serial_event_callback, flipper_vibrator); + + with_view_model( + flipper_vibrator->view, + FlipperVibratorModel * model, + { model->display_text = "Ready ^_^"; }, + true); + + } else { + FURI_LOG_E(TAG, "Please, enable the Bluetooth and restart the app"); + + with_view_model( + flipper_vibrator->view, + FlipperVibratorModel * model, + { model->display_text = "Error: Bluetooth not enabled"; }, + true); + } + } + return true; +} + +static void draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + FlipperVibratorModel* app = ctx; + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, (char*)app->display_text); +} + +FlipperVibrator* flipper_vibrator_alloc(FBP* fbp) { + furi_assert(fbp); + FlipperVibrator* flipper_vibrator = malloc(sizeof(FlipperVibrator)); + flipper_vibrator->view = view_alloc(); + flipper_vibrator->fbp = fbp; + view_set_context(flipper_vibrator->view, flipper_vibrator); + view_allocate_model(flipper_vibrator->view, ViewModelTypeLocking, sizeof(FlipperVibratorModel)); + view_set_draw_callback(flipper_vibrator->view, draw_callback); + view_set_input_callback(flipper_vibrator->view, input_callback); + + with_view_model( + flipper_vibrator->view, + FlipperVibratorModel * model, + { model->display_text = "Press OK to start"; }, + true); + + return flipper_vibrator; +} + +void flipper_vibrator_free(FlipperVibrator* flipper_vibrator) { + furi_assert(flipper_vibrator); + view_free(flipper_vibrator->view); + free(flipper_vibrator); +} + +View* flipper_vibrator_get_view(FlipperVibrator* flipper_vibrator) { + furi_assert(flipper_vibrator); + return flipper_vibrator->view; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/views/internal.h b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/views/internal.h new file mode 100644 index 000000000..40206bbff --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-bp/views/internal.h @@ -0,0 +1,12 @@ +#pragma once +#include +#include + +typedef struct FBP FBP; +typedef struct FlipperVibrator FlipperVibrator; + +FlipperVibrator* flipper_vibrator_alloc(FBP* fbp); + +void flipper_vibrator_free(FlipperVibrator* flipper_vibrator); + +View* flipper_vibrator_get_view(FlipperVibrator* flipper_vibrator); diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/.gitignore b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/.gitignore new file mode 100644 index 000000000..c6127b38c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/LICENSE.txt b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/LICENSE.txt new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/README.md new file mode 100644 index 000000000..138826184 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/README.md @@ -0,0 +1,22 @@ +# Flipper DCF77 +Sends the DCF77 time signal (badly) on the 125khz LFRFID antenna. This should allow you to update [radio clocks](https://en.wikipedia.org/wiki/Radio_clock). You can send it at the original 77.5kHz, but I've had better range using 155kHz. The baseband signal is output on C3. + +Building a portable antenna for the LF/VLF band is left as an exercise for the reader. + +# technical details + +* It works on every clock I own _eventually_. DCF77 is slow, it sends a complete update once per minute. Sometimes it works on the first try, sometimes I have to wait more than 5 attempts +* Implementation is using a software kernel timer (`furi_timer_start`) to generate the signal +* Implementation is wonky and although FZ is FreeRTOS, this software timer is hit and miss. 30Hz seems to work best for generating the baseband signal. More interrupts seem to overlap and are significantly delayed. Less interrupts are sometimes (still!) delayed or missed, but less interrupts means the baseband signal gets distorted more during a miss +* The baseband signal encodes 0 as an 800ms mark, 1 as a 900ms mark and second 59 of each minute is marked with the carrier being on continuously - this is the ASK modulation. There's also a PSK modulation which FZ is not doing. +* The transmitter is not off between marks, but is still transmitting at reduced power. This is rarely visible outside Germany +* The antenna is highly mistuned for this purpose (sending 77500Hz on a 125000Hz aerial is about 33% off). You can try the 155k harmonic, which is only 20% off. I have seen working attempts using speaker generated EMF at 15500 Hz. +* Combine the slow signal, wonky software timer and very mistuned antenna and YMMV + +# todo + +* menus! make it more flipper! +* configurable simulated data (encoding the time `25:69` is possible) +* simulate it just as a timezone offset (for changing clocks around your house according to your country's choice of DST madness) +* RBU, ALS162, WWVB, other time signals + diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/application.fam new file mode 100644 index 000000000..52ddf1e00 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/application.fam @@ -0,0 +1,12 @@ +App( + appid="dcf77", + name="DCF77 Transmitter", + apptype=FlipperAppType.PLUGIN, + entry_point="dcf77_app_main", + cdefines=["APP_DCF77"], + requires=["gui"], + stack_size=1 * 1024, + order=10, + fap_icon="icons/app.png", + fap_category="Misc_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/dcf77_app.c b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/dcf77_app.c new file mode 100644 index 000000000..86381de91 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/dcf77_app.c @@ -0,0 +1,445 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "dcf77_app.h" + +uint8_t get_dcf_message_bit(uint8_t* message, uint8_t bit) +{ + if (bit == 59 || bit == 0) + { + return 0; + } + + uint8_t byte_ = bit / 8; + uint8_t bit_ = bit % 8; + + uint8_t bit_value = *(message+byte_) & (1 << (7-bit_)); + + if (!!bit_value) + { + return 1; + } + return 0; +} + +// should it still be const? +static void update_dcf77_message_from_rtc(AppFSM* app_fsm) +{ + FuriHalRtcDateTime dt; + furi_hal_rtc_get_datetime(&dt); + app_fsm->bit_number = dt.second; + app_fsm->next_message = malloc(8); + /* + set_dcf_message(app_fsm->next_message, dt.minute, dt.hour, + dt.day, dt.month, (uint8_t)(dt.year % 100), dt.weekday, + false, false, false, false, 0x0000); + */ + + //set_dcf_message(app_fsm->next_message, 41, 17, 27, 12, 22, 2, false, false, false, false, 0x0000); + + app_fsm->tx_hour = 17; + app_fsm->tx_minute = 41; + app_fsm->tx_day = 27; + app_fsm->tx_month = 12; + app_fsm->tx_year = 22; + app_fsm->tx_dow = 2; + + + app_fsm->tx_hour = dt.hour; + app_fsm->tx_minute = dt.minute; + app_fsm->tx_day = dt.day; + app_fsm->tx_month = dt.month; + app_fsm->tx_year = dt.year % 100; + app_fsm->tx_dow = dt.weekday; + + set_dcf_message(app_fsm->next_message, app_fsm->tx_minute, app_fsm->tx_hour, app_fsm->tx_day, app_fsm->tx_month, + app_fsm->tx_year, app_fsm->tx_dow, false, false, false, false, 0x0000); + app_fsm->buffer_swap_pending = true; +} + +static void set_outputs(bool status, AppFSM* app_fsm) +{ + UNUSED(app_fsm); + static bool last; + if (last != status) + { + if (status) + { + furi_hal_light_set(LightRed, 0xFF); + furi_hal_light_set(LightGreen, 0x1F); + // furi_hal_speaker_start(50, 1); + + } + else + { + furi_hal_light_set(LightRed | LightGreen | LightBlue, 0); + // furi_hal_speaker_stop(); + } + } + last = status; +} + +static void comparator_trigger_callback(bool level, void *comp_ctx) { + UNUSED(comp_ctx); + furi_hal_gpio_write(&gpio_ext_pa7, !level); +} + + +void gpio_init() +{ + furi_hal_gpio_write(OUTPUT_PIN, false); + furi_hal_gpio_init(OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +} + +void gpio_mark() +{ + furi_hal_gpio_write(OUTPUT_PIN, true); +} + + +void gpio_space() +{ + furi_hal_gpio_write(OUTPUT_PIN, false); +} + +void gpio_deinit() +{ + furi_hal_gpio_init(OUTPUT_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedVeryHigh); +} + +void dcf77_lf_init(int freq, AppFSM* app_fsm) +{ + /* // this ends up doing + // LL_TIM_SetAutoReload(FURI_HAL_RFID_READ_TIMER, period); + // LL_TIM_OC_SetCompareCH1(FURI_HAL_RFID_READ_TIMER, period*duty_cycle); + */ + + furi_hal_rfid_tim_reset(); + furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); + furi_hal_rfid_comp_set_callback(comparator_trigger_callback, app_fsm); + furi_hal_rfid_pins_read(); + furi_hal_rfid_tim_read(freq, 0.5); +} + +void dcf77_mark(int freq) +{ + furi_hal_rfid_tim_reset(); + furi_hal_rfid_tim_read(freq, 0.5); + furi_hal_rfid_comp_start(); + furi_hal_rfid_tim_read_start(); + /* --- */ + furi_hal_rfid_comp_stop(); + furi_hal_rfid_tim_read_stop(); +} + +void dcf77_space() +{ + furi_hal_rfid_comp_stop(); + furi_hal_rfid_tim_read_stop(); +} + +void dcf77_deinit() +{ + dcf77_space(); + furi_hal_rfid_comp_stop(); + furi_hal_rfid_comp_set_callback(NULL, NULL); + furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog); + furi_hal_rfid_tim_read_stop(); + furi_hal_rfid_tim_reset(); + furi_hal_rfid_pins_reset(); +} + +static void render_callback(Canvas* const canvas, void* ctx) +{ + // const AppFSM* app_fsm = acquire_mutex((ValueMutex*)ctx, 25); + AppFSM* app_fsm = acquire_mutex((ValueMutex*)ctx, 25); + if(app_fsm == NULL) { + return; + } + + char buffer[64]; + uint8_t yoffset = 9; + uint8_t bit_number = app_fsm->bit_number; + uint8_t bit_value = app_fsm->bit_value; + uint8_t underline_x = (bit_number/8) * 12 + 16; + uint8_t byte_ = app_fsm->dcf77_message[bit_number/8]; + char display_bits[9] = "00000000"; + + if (app_fsm->buffer_swap_pending) + { + memcpy(app_fsm->dcf77_message, app_fsm->next_message, 8); + canvas_draw_str_aligned(canvas, 112, 10, AlignCenter, AlignBottom, "*"); + app_fsm->buffer_swap_pending = false; + } + + + canvas_draw_frame(canvas, 0, 0, 128, 64); + canvas_set_font(canvas, FontPrimary); + snprintf(buffer, 64, "%1x.%1x=%01x", bit_number/8, (bit_number%8), bit_value); + FuriHalRtcDateTime dt; + furi_hal_rtc_get_datetime(&dt); + //canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignBottom, "DCF77 emulator"); + snprintf(buffer, 64, "%02d:%02d %02d.%02d.%02d", app_fsm->tx_hour, app_fsm->tx_minute, app_fsm->tx_day, app_fsm->tx_month, + app_fsm->tx_year); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignBottom, buffer); + + if (app_fsm->debug_flag) + { + canvas_draw_str_aligned(canvas, 8, 10, AlignCenter, AlignBottom, "D"); + } + + snprintf(buffer, 64, "%1x.%1x=%01x", bit_number/8, (bit_number%8), bit_value); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 24 + (9 - yoffset), AlignCenter, AlignBottom, buffer); + + snprintf(buffer, 64, "%s (bit %d)", dcf77_bitnames[bit_number], bit_value); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 34 + (9 - yoffset), AlignCenter, AlignBottom, buffer); + + + + snprintf(buffer, 64, "%02x%02x%02x%02x%02x%02x%02x%02x", + app_fsm->dcf77_message[0], app_fsm->dcf77_message[1], app_fsm->dcf77_message[2], app_fsm->dcf77_message[3], + app_fsm->dcf77_message[4], app_fsm->dcf77_message[5], app_fsm->dcf77_message[6], app_fsm->dcf77_message[7] ); + canvas_set_font(canvas, FontKeyboard); + canvas_draw_str_aligned(canvas, 64, 44 + (9 - yoffset), AlignCenter, AlignBottom, buffer); + + + canvas_draw_line(canvas, underline_x, 45, underline_x+10, 45); + for(int i = 0; i < 8; i++) + { + if ((byte_ & (1 << (7-i))) != 0) + { + display_bits[i] = '1'; + } + } + + canvas_set_font(canvas, FontKeyboard); + snprintf(buffer, 64, "%02x=%s", byte_, display_bits); + canvas_draw_str_aligned(canvas, 64, 54 + (9 - yoffset), AlignCenter, AlignBottom, buffer); // whole message + underline_x = (bit_number%8) * 6 + 49; + canvas_draw_line(canvas, underline_x, 55, underline_x+4, 55); // current byte + + release_mutex((ValueMutex*)ctx, app_fsm); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + AppEvent event = {.type = EventKeyPress, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void timer_tick_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + AppEvent event = {.type = EventTimerTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void app_init(AppFSM* const app_fsm, FuriMessageQueue* event_queue) { + + app_fsm->counter = 0; + app_fsm->dcf77_message = &dcf77_message_buffer[0]; + app_fsm->next_message = &dcf77_next_buffer[0]; + + dcf77_lf_init(LF_FREQ, app_fsm); + gpio_init(); + + app_fsm->dcf77_message = malloc(8); // message had 60 bits since 1959 and is unlikely to change + // app_fsm->next_message = malloc(8); + update_dcf77_message_from_rtc(app_fsm); + app_fsm->baseband_counter = 0; + + app_fsm->_event_queue = event_queue; + FuriTimer* timer = furi_timer_alloc(timer_tick_callback, FuriTimerTypePeriodic, app_fsm->_event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / TIMER_HZ); + app_fsm->_timer = timer; +} + +static void app_deinit(AppFSM* const app_fsm) { + dcf77_deinit(); + gpio_deinit(); + app_fsm->buffer_swap_pending = false; + free(app_fsm->dcf77_message); + furi_timer_free(app_fsm->_timer); +} + +static void on_timer_tick(AppFSM* app_fsm) +{ + static uint8_t last_second = 61; + static bool last_output = false; + bool output = true; + + FuriHalRtcDateTime dt; + furi_hal_rtc_get_datetime(&dt); + app_fsm->bit_number = dt.second; + app_fsm->bit_value = get_dcf_message_bit(app_fsm->dcf77_message, app_fsm->bit_number); + + if (dt.second != last_second) + { + app_fsm->baseband_counter = 0; + output = true; + } + else + { + app_fsm->baseband_counter++; + } + + /* + if (app_fsm->baseband_counter < 8) + { + app_fsm->debug_flag = true; + } + else + { + app_fsm->debug_flag = false; + }*/ + + if (dt.second == 0 && app_fsm->baseband_counter == 3) + { + update_dcf77_message_from_rtc(app_fsm); + } + + if (dt.second == 59) + { + output = true; + } + else + { + if (app_fsm->baseband_counter > TIME_ZERO && app_fsm->bit_value == 0) + { + output = false; + + } + else if (app_fsm->baseband_counter > TIME_ONE && app_fsm->bit_value == 1) + { + output = false; + } + } + + + if (last_output != output) + { + set_outputs(output, app_fsm); + if (!output) + { + dcf77_space(); + gpio_space(); + } + else + { + dcf77_mark(LF_FREQ); + gpio_mark(); + } + } + + last_second = dt.second; + last_output = output; +} + + +int32_t dcf77_app_main(void* p) { + UNUSED(p); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent)); + AppFSM* app_fsm = malloc(sizeof(AppFSM)); + app_init(app_fsm, event_queue); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, app_fsm, sizeof(AppFSM))) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + free(app_fsm); + return 255; + } + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message_block(notification, &sequence_display_backlight_enforce_on); + + + if (furi_hal_speaker_acquire(500)) + { + ; + } + + + DOLPHIN_DEED(DolphinDeedPluginGameStart); + + AppEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + + AppFSM* app_fsm = (AppFSM*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // kepress events + if(event.type == EventKeyPress) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + app_fsm->last_key = KeyUp; + break; + case InputKeyDown: + app_fsm->last_key = KeyDown; + break; + case InputKeyRight: + app_fsm->last_key = KeyRight; + break; + case InputKeyLeft: + app_fsm->last_key = KeyLeft; + break; + case InputKeyOk: + app_fsm->last_key = KeyOK; + app_fsm->counter = -5; + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + // user events + } else if(event.type == EventTimerTick) { + FURI_CRITICAL_ENTER(); + on_timer_tick(app_fsm); + FURI_CRITICAL_EXIT(); + } + } else { + // event timeout + } + + view_port_update(view_port); + release_mutex(&state_mutex, app_fsm); + } + furi_hal_speaker_release(); + notification_message_block(notification, &seq_c_minor); + + // Wait for all notifications to be played and return backlight to normal state + app_deinit(app_fsm); + + notification_message_block(notification, &sequence_display_backlight_enforce_auto); + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + free(app_fsm); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/dcf77_app.h b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/dcf77_app.h new file mode 100644 index 000000000..94e06495a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/dcf77_app.h @@ -0,0 +1,168 @@ +#ifndef __ARHA_FLIPPERAPP_DEMO +#define __ARHA_FLIPPERAPP_DEMO + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "dcf77_util.h" + +// the TAG is used for displaying a relevant prefix in logs. update it. +#define TAG "__ARHA_FLIPPERAPP" +#define TIMER_HZ 30 +#define TIME_ZERO 24 +#define TIME_ONE 27 +#define LF_FREQ 77500 * 2 +#define OUTPUT_PIN &gpio_ext_pc3 +// #define TIME_ZERO 15 +// #define TIME_ONE 5 + + +uint8_t dcf77_message_buffer[8]; +uint8_t dcf77_next_buffer[8]; + +typedef enum { + KeyNone, + KeyUp, + KeyRight, + KeyDown, + KeyLeft, + KeyOK +} KeyCode; + + +typedef enum { + EventTimerTick, + EventKeyPress, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} AppEvent; + +typedef struct { + uint16_t len; + KeyCode last_key; + + FuriTimer* _timer; + FuriMessageQueue* _event_queue; + + int counter; + + uint8_t bit_number; // 0 - 59 + uint8_t bit_value; // 0 or 1 for actual bits, 2 for end-of-minute marker + uint8_t baseband_counter; // 0 - 20, so we can generate 800 and 900 ms wide pulses (bit0 = 800ms = 16; bit1 = 900ms = 18; bit2 = 1000ms = 20) + uint8_t* dcf77_message; // these are 8 bytes which encode, LSB, every bit in the DCF77 message. see set_dcf_message() + uint8_t* next_message; + + bool buffer_swap_pending; + bool debug_flag; + + uint8_t tx_minute; + uint8_t tx_hour; + uint8_t tx_day; + uint8_t tx_month; + uint8_t tx_year; + uint8_t tx_dow; +} AppFSM; + + +const NotificationSequence seq_c_minor = { + &message_note_c4, + &message_delay_100, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_100, + &message_sound_off, + &message_delay_10, + + &message_note_g4, + &message_delay_100, + &message_sound_off, + &message_delay_10, + + &message_vibro_on, + &message_delay_50, + &message_vibro_off, + NULL, +}; + +const char *dcf77_bitnames[] = { + "Start minute", + "Civil 1", + "Civil 2", + "Civil 3", + "Civil 4", + "Civil 5", + "Civil 6", + "Civil 7", + "Civil 8", + "Civil 9", + "Civil 10", + "Civil 11", + "Civil 12", + "Civil 13", + "Civil 14", + "Abnormal", + "DST change", + "UTC+02", + "UTC+01", + "Leap sec", + "Start time", + "Minutes 1", + "Minutes 2", + "Minutes 3", + "Minutes 4", + "Minutes 5", + "Minutes 6", + "Minutes 7", + "Minutes P", + "Hours 1", + "Hours 2", + "Hours 3", + "Hours 4", + "Hours 5", + "Hours 6", + "Hours P", + "Day 1", + "Day 2", + "Day 3", + "Day 4", + "Day 5", + "Day 6", + "Weekday 1", + "Weekday 2", + "Weekday 4", + "Month 1", + "Month 2", + "Month 3", + "Month 4", + "Month 5", + "Year 1", + "Year 2", + "Year 3", + "Year 4", + "Year 5", + "Year 6", + "Year 7", + "Year 8", + "Date P", + "End" +}; + +#endif \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/dcf77_util.c b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/dcf77_util.c new file mode 100644 index 000000000..3ed2dd1cf --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/dcf77_util.c @@ -0,0 +1,135 @@ +#include "dcf77_util.h" + +void set_dcf_message(uint8_t* dest, uint8_t minute, uint8_t hour, + uint8_t day, uint8_t month, uint8_t year, uint8_t dow, + bool dst, bool predst, bool abnormal, bool leap, uint16_t civbits) +{ + uint8_t bcd_minute = 0; + uint8_t bcd_hour = 0; + uint8_t bcd_day = 0; + uint8_t bcd_month = 0; + uint8_t bcd_year = 0; + + /* converting binary to bcd (which looks like hex, without the a-f digits) + this is very common in old-school electronics */ + bcd_minute = ((minute / 10) << 4) | (minute % 10); + bcd_hour = ((hour / 10) << 4) | (hour % 10); + bcd_day = ((day / 10) << 4) | (day % 10); + bcd_month = ((month / 10) << 4) | (month % 10); + bcd_year = ((year / 10) << 4) | (year % 10); + + /* parity bits */ + bool p_minute = 1; + bool p_hour = 1; + bool p_date = 1; + + for (int i = 0; i < 8; i++) + { + p_minute ^= !!(bcd_minute & (1 << i)); + p_hour ^= !!(bcd_hour & (1 << i)); + + p_date = !!(bcd_day & (1 << i)); + p_date = !!(bcd_month & (1 << i)); + p_date = !!(bcd_year & (1 << i)); + p_date = !!((dow & 0x07) & (1 << i)); + } + + dest[0] = (uint8_t)(civbits >> 7); + dest[1] = ((civbits & 0x7F) << 1) | abnormal; + + // d2: 16-23; 16=adst, 17=dst, 18=!dsst, 19=leap, 20=1, 21-22-23 are minute lsb 1, 2, 3 + dest[2] = (predst << 7) | (dst << 6) | (!dst << 5) | (leap << 4) | (1 << 3); + dest[2]|= BBIT(bcd_minute, 0) << 2; + dest[2]|= BBIT(bcd_minute, 1) << 1; + dest[2]|= BBIT(bcd_minute, 2) << 0; + //dest[2]|= (bcd_minute & (1 << 2)); + + dest[3] =0; + // DCF bit 24 is byte 3, bit 7 + dest[3]|= BBIT(bcd_minute, 3) << 7; + // DCF bit 25 is byte 3, bit 6 + dest[3]|= BBIT(bcd_minute, 4) << 6; + // DCF bit 26 is byte 3, bit 5 + dest[3]|= BBIT(bcd_minute, 5) << 5; + // DCF bit 27 is byte 3, bit 4 + dest[3]|= BBIT(bcd_minute, 6) << 4; + // DCF bit 28 is byte 3, bit 3 (E parity, minutes) + dest[3]|= p_minute << 3; + // DCF bit 29 is byte 3, bit 2 + dest[3]|= BBIT(bcd_hour, 0) << 2; + // DCF bit 30 is byte 3, bit 1 + dest[3]|= BBIT(bcd_hour, 1) << 1; + // DCF bit 31 is byte 3, bit 0 + dest[3]|= BBIT(bcd_hour, 2) << 0; + + // d3: 24-31, 24-27 min lsb 3-6; 28=p, 29-31: hours lsb 0-2 + //dest[3] = bcd_minute << 3 | (p_minute << 3); + + // d4: 32-39 + dest[4] =0; + // DCF bit 32 is byte 4, bit 7 + dest[4]|= BBIT(bcd_hour, 3) << 7; + // DCF bit 33 is byte 4, bit 6 + dest[4]|= BBIT(bcd_hour, 4) << 6; + // DCF bit 34 is byte 4, bit 5 + dest[4]|= BBIT(bcd_hour, 5) << 5; + // DCF bit 35 is byte 4, bit 4 + dest[4]|= p_hour << 4; + // DCF bit 36 is byte 4, bit 3 + dest[4]|= BBIT(bcd_day, 0) << 3; + // DCF bit 37 is byte 4, bit 2 + dest[4]|= BBIT(bcd_day, 1) << 2; + // DCF bit 38 is byte 4, bit 1 + dest[4]|= BBIT(bcd_day, 2) << 1; + // DCF bit 39 is byte 4, bit 0 + dest[4]|= BBIT(bcd_day, 3) << 0; + + // d5: 40-47, 40-41: day lsb 4-5, 42-44: dow; 45-47: month lsb 0-2 + dest[5] =0; + // DCF bit 40 is byte 5, bit 7 + dest[5]|= BBIT(bcd_day, 4) << 7; + // DCF bit 41 is byte 5, bit 6 + dest[5]|= BBIT(bcd_day, 5) << 6; + // DCF bit 42 is byte 5, bit 5 + dest[5]|= BBIT(dow, 0) << 5; + // DCF bit 43 is byte 5, bit 4 + dest[5]|= BBIT(dow, 1) << 4; + // DCF bit 44 is byte 5, bit 3 + dest[5]|= BBIT(dow, 2) << 3; + // DCF bit 45 is byte 5, bit 2 + dest[5]|= BBIT(bcd_month, 0) << 2; + // DCF bit 46 is byte 5, bit 1 + dest[5]|= BBIT(bcd_month, 1) << 1; + // DCF bit 47 is byte 5, bit 0 + dest[5]|= BBIT(bcd_month, 2) << 0; + + + // d6: 48-55; 48-49 month lsb 3-4; 50-55: year lsb 0-5 + dest[6] =0; + // DCF bit 48 is byte 6, bit 7 + dest[6]|= BBIT(bcd_month, 3) << 7; + // DCF bit 49 is byte 6, bit 6 + dest[6]|= BBIT(bcd_month, 4) << 6; + // DCF bit 50 is byte 6, bit 5 + dest[6]|= BBIT(bcd_year, 0) << 5; + // DCF bit 51 is byte 6, bit 4 + dest[6]|= BBIT(bcd_year, 1) << 4; + // DCF bit 52 is byte 6, bit 3 + dest[6]|= BBIT(bcd_year, 2) << 3; + // DCF bit 53 is byte 6, bit 2 + dest[6]|= BBIT(bcd_year, 3) << 2; + // DCF bit 54 is byte 6, bit 1 + dest[6]|= BBIT(bcd_year, 4) << 1; + // DCF bit 55 is byte 6, bit 0 + dest[6]|= BBIT(bcd_year, 5) << 0; + + + // d7: 56-63; 56-57: year lsb 6-7; 58: date parity; 59: special minute marker + dest[7] =0; + // DCF bit 56 is byte 7, bit 7 + dest[7]|= BBIT(bcd_year, 6) << 7; + // DCF bit 57 is byte 7, bit 6 + dest[7]|= BBIT(bcd_year, 7) << 6; + // DCF bit 58 is byte 7, bit 5 + dest[7]|= p_date << 5; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/dcf77_util.h b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/dcf77_util.h new file mode 100644 index 000000000..31fda0ebb --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/dcf77_util.h @@ -0,0 +1,18 @@ +#ifndef __arha_dcf77util_h +#define __arha_dcf77util_h + +#include +#include +#include + +void set_dcf_message(uint8_t* dest, uint8_t minute, uint8_t hour, + uint8_t day, uint8_t month, uint8_t year, uint8_t dow, + bool dst, bool predst, bool abnormal, bool leap, uint16_t civbits); + +#ifndef BBIT +#define BBIT(B,shift) (!!(B & (1 << shift))) +#endif + + + +#endif \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/icons/app.png b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/icons/app.png new file mode 100644 index 000000000..db6dcf7fb Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper-dcf77/icons/app.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/LICENSE new file mode 100644 index 000000000..28d693a7c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 MX + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/README.md new file mode 100644 index 000000000..a40cb2d5a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/README.md @@ -0,0 +1,7 @@ +# Flashlight Plugin for Flipper Zero + +Simple Flashlight special for @Svaarich by @xMasterX + +Enables 3.3v on pin 7/C3 and leaves it on when you exit app + +**Connect LED to (+ -> 7/C3) | (GND -> GND)** diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/application.fam new file mode 100644 index 000000000..633b76a1f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/application.fam @@ -0,0 +1,14 @@ +App( + appid="flashlight", + name="Flashlight", + apptype=FlipperAppType.EXTERNAL, + entry_point="flashlight_app", + cdefines=["APP_FLASHLIGHT"], + requires=[ + "gui", + ], + stack_size=2 * 1024, + order=20, + fap_icon="flash10px.png", + fap_category="GPIO_Extra", +) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/flash10px.png b/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/flash10px.png new file mode 100644 index 000000000..963a9ab5f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/flash10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/flashlight.c b/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/flashlight.c new file mode 100644 index 000000000..534d48fdb --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper-flashlight/flashlight.c @@ -0,0 +1,130 @@ +// by @xMasterX + +#include +#include +#include +#include +#include +#include + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + bool is_on; +} PluginState; + +static void render_callback(Canvas* const canvas, void* ctx) { + const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); + if(plugin_state == NULL) { + return; + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Flashlight"); + + canvas_set_font(canvas, FontSecondary); + + if(!plugin_state->is_on) { + elements_multiline_text_aligned( + canvas, 64, 28, AlignCenter, AlignTop, "Press OK button turn on"); + } else { + elements_multiline_text_aligned(canvas, 64, 28, AlignCenter, AlignTop, "Light is on!"); + elements_multiline_text_aligned( + canvas, 64, 40, AlignCenter, AlignTop, "Press OK button to off"); + } + + release_mutex((ValueMutex*)ctx, plugin_state); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void flash_toggle(PluginState* const plugin_state) { + furi_hal_gpio_write(&gpio_ext_pc3, false); + furi_hal_gpio_init(&gpio_ext_pc3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + if(plugin_state->is_on) { + furi_hal_gpio_write(&gpio_ext_pc3, false); + plugin_state->is_on = false; + } else { + furi_hal_gpio_write(&gpio_ext_pc3, true); + plugin_state->is_on = true; + } +} + +int32_t flashlight_app() { + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + PluginState* plugin_state = malloc(sizeof(PluginState)); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + FURI_LOG_E("flashlight", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(plugin_state); + return 255; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + + PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + case InputKeyDown: + case InputKeyRight: + case InputKeyLeft: + break; + case InputKeyOk: + flash_toggle(plugin_state); + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } + } + + view_port_update(view_port); + release_mutex(&state_mutex, plugin_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_analog_clock/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipper_analog_clock/README.md new file mode 100644 index 000000000..7c83f35cc --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_analog_clock/README.md @@ -0,0 +1 @@ +# Analog Clock app for Flipper Zero diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_analog_clock/analog_clock.c b/Applications/Official/DEV_FW/source/xMasterX/flipper_analog_clock/analog_clock.c new file mode 100644 index 000000000..531dadd52 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_analog_clock/analog_clock.c @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include +#include + +#define PI 3.14 + +typedef struct { + uint8_t x; + uint8_t y; +} Vector2; + +typedef enum { + EventTypeTick, + EventTypeInput, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} AppEvent; + +typedef struct { + FuriMutex* mutex; + FuriHalRtcDateTime date_time; +} ClockApp; + +static Vector2 angle_to_vector2(float angle_in_degrees, uint8_t distance, Vector2 center) { + float radians = (angle_in_degrees - 90) * (PI / 180); + + Vector2 vec = { + .x = center.x + cos(radians) * distance, + .y = center.y + sin(radians) * distance, + }; + + return vec; +} + +static void analog_clock_app_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + ClockApp* app = context; + furi_mutex_acquire(app->mutex, FuriWaitForever); + + uint8_t width = canvas_width(canvas); + uint8_t height = canvas_height(canvas); + Vector2 clock_center = { + .x = 28 + width / 2, + .y = height / 2, + }; + uint8_t radius = MIN(width, height) / 2 - 2; + + canvas_draw_circle(canvas, clock_center.x, clock_center.y, radius); + + FuriString* str = furi_string_alloc(); + + for(uint8_t i = 3; i <= 12; i += 3) { + Vector2 pos = angle_to_vector2(360 / 12 * i, radius - 4, clock_center); + + furi_string_printf(str, "%i", i); + + canvas_draw_str_aligned( + canvas, pos.x, pos.y, AlignCenter, AlignCenter, furi_string_get_cstr(str)); + } + + Vector2 hour_vec = + angle_to_vector2(((app->date_time.hour % 12) / 12.f * 360.f), radius - 8, clock_center); + canvas_draw_line(canvas, clock_center.x, clock_center.y, hour_vec.x, hour_vec.y); + + Vector2 minute_vec = + angle_to_vector2((app->date_time.minute / 60.f * 360.f), radius - 4, clock_center); + canvas_draw_line(canvas, clock_center.x, clock_center.y, minute_vec.x, minute_vec.y); + + Vector2 second_vec = + angle_to_vector2((app->date_time.second / 60.f * 360.f), radius - 2, clock_center); + canvas_draw_line(canvas, clock_center.x, clock_center.y, second_vec.x, second_vec.y); + + canvas_set_font(canvas, FontSecondary); + + locale_format_date(str, &app->date_time, locale_get_date_format(), "."); + uint16_t date_str_width = canvas_string_width(canvas, furi_string_get_cstr(str)); + canvas_draw_frame(canvas, 0, 51, date_str_width + 6, 13); + canvas_draw_str(canvas, 3, 61, furi_string_get_cstr(str)); + + furi_string_free(str); + furi_mutex_release(app->mutex); +} + +static void analog_clock_app_input_callback(InputEvent* event, void* context) { + furi_assert(context); + FuriMessageQueue* event_queue = context; + AppEvent app_event = {.type = EventTypeInput, .input = *event}; + furi_message_queue_put(event_queue, &app_event, FuriWaitForever); +} + +static void analog_clock_app_tick(void* context) { + furi_assert(context); + FuriMessageQueue* event_queue = context; + AppEvent app_event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &app_event, 0); +} + +int32_t analog_clock_app(void* p) { + UNUSED(p); + + ClockApp* app = malloc(sizeof(ClockApp)); + furi_hal_rtc_get_datetime(&app->date_time); + + app->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent)); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, analog_clock_app_draw_callback, app); + view_port_input_callback_set(view_port, analog_clock_app_input_callback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + FuriTimer* timer = furi_timer_alloc(analog_clock_app_tick, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second + + AppEvent event; + for(bool running = true; running;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + if(event_status == FuriStatusOk) { + if(event.type == EventTypeInput) { + if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) { + running = false; + } + } else if(event.type == EventTypeTick) { + furi_mutex_acquire(app->mutex, FuriWaitForever); + + furi_hal_rtc_get_datetime(&app->date_time); + + furi_mutex_release(app->mutex); + view_port_update(view_port); + } + } + } + + furi_timer_free(timer); + + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(event_queue); + + furi_mutex_free(app->mutex); + free(app); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_analog_clock/analog_clock.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_analog_clock/analog_clock.png new file mode 100644 index 000000000..8a5406e6c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_analog_clock/analog_clock.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_analog_clock/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipper_analog_clock/application.fam new file mode 100644 index 000000000..5f59e5a20 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_analog_clock/application.fam @@ -0,0 +1,17 @@ +# For details & more options, see documentation/AppManifests.md in firmware repo + +App( + appid="analog_clock", # Must be unique + name="Analog Clock", # Displayed in UI + apptype=FlipperAppType.EXTERNAL, + entry_point="analog_clock_app", + stack_size=2 * 1024, + fap_category="Misc_Extra", + # Optional values + # fap_version=(0, 1), # (major, minor) + fap_icon="analog_clock.png", # 10x10 1-bit PNG + # fap_description="A simple app", + # fap_author="J. Doe", + # fap_weburl="https://github.com/user/analog_clock", + # fap_icon_assets="images", # Image assets to compile for this application +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/README.md new file mode 100644 index 000000000..21eb7e4b7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/README.md @@ -0,0 +1,64 @@ +# flipperzero-geigercounter +A geiger counter application for the Flipper Zero + +![banner](https://github.com/nmrr/flipperzero-geigercounter/blob/main/img/logo.jpg) + +You need a **geiger counter** board to run this application. This board can be used : https://aliexpress.com/item/1005004074447209.html + +The geiger counter board can be powered with +5V power pin of the **Flipper Zero** + +Output pin for measure on arduino cannot be used on the **Flipper Zero** because output voltage is too low. You can use jack out port instead. This port must be connected on **A7** GPIO : + +

+ +## Build the program + +Assuming the toolchain is already installed, copy **flipper_geiger** directory to **applications_user** + +Plug your **Flipper Zero** and build the geiger counter : +``` +./fbt launch_app APPSRC=applications_user/flipper_geiger +``` + +The program will automatically be launched after compilation + + + +**A4** GPIO can be connected on **A7** GPIO to test this application without using a geiger tube. **A4** GPIO is generating a signal whose frequency changes every second. + +Press Ok button to clear the graph, press back button to quit + +If you don't want to build this application, just simply copy **flipper_geiger.fap** on your **Flipper Zero** + +## Use cases + +Ambient radioactivity : + + + +Measure of uranium ore piece inside a lead container : + + + +Measure of uranium ore piece in contact with the geiger tube : + + + +All previous measures in a row (the scale of the graph is automatically adjusted) : + + + +**A4** GPIO on **A7** GPIO : + + + +## Changelog + +* 2023-01-15 + * Code fix & a lot of optimizations. Now a lot of events can be handled without any issue + +* 2023-01-09 + * Schematic was added & code fix + +* 2023-01-08 + * Initial release \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/application.fam new file mode 100644 index 000000000..5bbbddf99 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/application.fam @@ -0,0 +1,13 @@ +App( + appid="flipper_geiger", + name="Geiger counter", + apptype=FlipperAppType.EXTERNAL, + entry_point="flipper_geiger_app", + cdefines=["APP_GEIGER"], + requires=[ + "gui", + ], + stack_size=1 * 1024, + fap_icon="geiger.png", + fap_category="GPIO_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/flipper_geiger.c b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/flipper_geiger.c new file mode 100644 index 000000000..2ed83a9fc --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/flipper_geiger.c @@ -0,0 +1,190 @@ +// CC0 1.0 Universal (CC0 1.0) +// Public Domain Dedication +// https://github.com/nmrr + +#include +#include +#include +#include +#include +#include +#include + +#define SCREEN_SIZE_X 128 +#define SCREEN_SIZE_Y 64 + +typedef enum { + EventTypeInput, + ClockEventTypeTick, + EventGPIO, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} EventApp; + +typedef struct { + uint32_t cps, cpm; + uint32_t line[SCREEN_SIZE_X/2]; + float coef; +} mutexStruct; + +static void draw_callback(Canvas* canvas, void* ctx) +{ + UNUSED(ctx); + + mutexStruct displayStruct; + mutexStruct* lfsrMutex = (mutexStruct*)acquire_mutex_block((ValueMutex*)ctx); + memcpy(&displayStruct, lfsrMutex, sizeof(mutexStruct)); + release_mutex((ValueMutex*)ctx, lfsrMutex); + + char buffer[32]; + snprintf(buffer, sizeof(buffer), "%ld cps - %ld cpm", displayStruct.cps, displayStruct.cpm); + + for (int i=0;iline[SCREEN_SIZE_X/2-1-i] = lfsrMutex->line[SCREEN_SIZE_X/2-2-i]; + + lfsrMutex->line[0] = counter; + lfsrMutex->cps = counter; + counter = 0; + + lfsrMutex->cpm = lfsrMutex->line[0]; + uint32_t max = lfsrMutex->line[0]; + for (int i=1;icpm += lfsrMutex->line[i]; + if (lfsrMutex->line[i] > max) max = lfsrMutex->line[i]; + } + + if (max > 0) lfsrMutex->coef = ((float)(SCREEN_SIZE_Y-15))/((float)max); + else lfsrMutex->coef = 1; + + screenRefresh = 1; + release_mutex(&state_mutex, lfsrMutex); + } + else if (event.type == EventGPIO) + { + counter++; + } + } + + if (screenRefresh == 1) view_port_update(view_port); + } + + furi_hal_gpio_disable_int_callback(&gpio_ext_pa7); + furi_hal_gpio_remove_int_callback(&gpio_ext_pa7); + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_timer_free(timer); + furi_record_close(RECORD_GUI); + + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/geiger.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/geiger.png new file mode 100644 index 000000000..d41e1915b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/geiger.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper1.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper1.png new file mode 100644 index 000000000..e517bedbf Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper2.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper2.png new file mode 100644 index 000000000..52d6d77d4 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper3.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper3.png new file mode 100644 index 000000000..99636bdde Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper4.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper4.png new file mode 100644 index 000000000..1190b18e3 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper5.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper5.png new file mode 100644 index 000000000..45f74701e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper5.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper6.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper6.png new file mode 100644 index 000000000..b3ebbf174 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/flipper6.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/logo.jpg b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/logo.jpg new file mode 100644 index 000000000..4d3e714e5 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/logo.jpg differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/schematic.jpg b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/schematic.jpg new file mode 100644 index 000000000..d6072596e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_geiger/img/schematic.jpg differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/.clang-format b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/.clang-format new file mode 100644 index 000000000..4b76f7fa4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/.clang-format @@ -0,0 +1,191 @@ +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: AlwaysBreak +AlignArrayOfStructures: None +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: false +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 99 +CommentPragmas: '^ IWYU pragma:' +QualifierAlignment: Leave +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +PackConstructorInitializers: BinPack +BasedOnStyle: '' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowAllConstructorInitializersOnNextLine: true +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: false +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequires: false +IndentWidth: 4 +IndentWrappedFunctionNames: true +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +LambdaBodyIndentation: Signature +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 10 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +PPIndentWidth: -1 +ReferenceAlignment: Pointer +ReflowComments: false +RemoveBracesLLVM: false +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: Never +SortJavaStaticImport: Before +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: Never +SpaceBeforeParensOptions: + AfterControlStatements: false + AfterForeachMacros: false + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: false + AfterOverloadedOperator: false + BeforeNonEmptyParentheses: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: c++03 +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +... + diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/.editorconfig b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/.editorconfig new file mode 100644 index 000000000..a31ef8e75 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.{cpp,h,c,py,sh}] +indent_style = space +indent_size = 4 + +[{Makefile,*.mk}] +indent_size = tab diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/.gitignore b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/.gitignore new file mode 100644 index 000000000..9bf2d4d7d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/.gitignore @@ -0,0 +1,54 @@ +dist/ + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/LICENSE new file mode 100644 index 000000000..85e363072 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Skurydin Alexey + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/README.md new file mode 100644 index 000000000..fbeb41680 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/README.md @@ -0,0 +1,4 @@ +# flipper_passgen +This is a simple Password Generator plugin (**fap**) for the [Flipper Zero](https://www.flipperzero.one). + +![preview](images/preview.png) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/application.fam new file mode 100644 index 000000000..ebc879f1d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/application.fam @@ -0,0 +1,12 @@ +App( + appid="passgen", + name="Password Generator", + apptype=FlipperAppType.PLUGIN, + entry_point="passgenapp", + requires=[ + "gui", + ], + fap_category="Misc_Extra", + fap_icon="icons/passgen_icon.png", + fap_icon_assets="icons", +) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/Horizontal_arrow_9x7.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/Horizontal_arrow_9x7.png new file mode 100644 index 000000000..caca88718 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/Horizontal_arrow_9x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/Ok_btn_9x9.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/Ok_btn_9x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/Pin_back_arrow_10x8.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/Pin_back_arrow_10x8.png new file mode 100644 index 000000000..3bafabd14 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/Pin_back_arrow_10x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/Vertical_arrow_7x9.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/Vertical_arrow_7x9.png new file mode 100644 index 000000000..b889fc8f3 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/Vertical_arrow_7x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/passgen_icon.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/passgen_icon.png new file mode 100644 index 000000000..1ed4f77fc Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/icons/passgen_icon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/images/preview.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/images/preview.png new file mode 100644 index 000000000..3224b0a8d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/images/preview.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/passgen.c b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/passgen.c new file mode 100644 index 000000000..12cdc10fb --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_passgen/passgen.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include +#include +#include + +#define PASSGEN_MAX_LENGTH 16 +#define PASSGEN_CHARACTERS_LENGTH (26 * 4) + +#define PASSGEN_DIGITS "0123456789" +#define PASSGEN_LETTERS_LOW "abcdefghijklmnopqrstuvwxyz" +#define PASSGEN_LETTERS_UP "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define PASSGEN_SPECIAL "!#$%^&*.-_" + +typedef enum PassGen_Alphabet { + Digits = 1, + Lowercase = 2, + + Uppercase = 4, + Special = 8, + + DigitsLower = Digits | Lowercase, + DigitsAllLetters = Digits | Lowercase | Uppercase, + Mixed = DigitsAllLetters | Special +} PassGen_Alphabet; + +const int AlphabetLevels[] = {Digits, Lowercase, DigitsLower, DigitsAllLetters, Mixed}; +const char* AlphabetLevelNames[] = {"1234", "abcd", "ab12", "Ab12", "Ab1#"}; +const int AlphabetLevelsCount = sizeof(AlphabetLevels) / sizeof(int); + +const NotificationSequence PassGen_Alert_vibro = { + &message_vibro_on, + &message_blue_255, + &message_delay_50, + &message_vibro_off, + NULL, +}; + +typedef struct { + FuriMessageQueue* input_queue; + ViewPort* view_port; + Gui* gui; + FuriMutex** mutex; + NotificationApp* notify; + char password[PASSGEN_MAX_LENGTH + 1]; + char alphabet[PASSGEN_CHARACTERS_LENGTH + 1]; + int length; + int level; +} PassGen; + +void state_free(PassGen* app) { + gui_remove_view_port(app->gui, app->view_port); + furi_record_close(RECORD_GUI); + view_port_free(app->view_port); + furi_message_queue_free(app->input_queue); + furi_mutex_free(app->mutex); + furi_record_close(RECORD_NOTIFICATION); + free(app); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + PassGen* app = ctx; + if(input_event->type == InputTypeShort) { + furi_message_queue_put(app->input_queue, input_event, 0); + } +} + +static void render_callback(Canvas* canvas, void* ctx) { + char str_length[8]; + PassGen* app = ctx; + furi_check(furi_mutex_acquire(app->mutex, FuriWaitForever) == FuriStatusOk); + + canvas_clear(canvas); + canvas_draw_box(canvas, 0, 0, 128, 14); + canvas_set_color(canvas, ColorWhite); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 11, "Password Generator"); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, 64, 35, AlignCenter, AlignCenter, app->password); + + // Navigation menu: + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 96, 52, &I_Pin_back_arrow_10x8); + canvas_draw_str(canvas, 108, 60, "Exit"); + + canvas_draw_icon(canvas, 54, 52, &I_Vertical_arrow_7x9); + canvas_draw_str(canvas, 64, 60, AlphabetLevelNames[app->level]); + + snprintf(str_length, sizeof(str_length), "Len: %d", app->length); + canvas_draw_icon(canvas, 4, 53, &I_Horizontal_arrow_9x7); + canvas_draw_str(canvas, 15, 60, str_length); + + furi_mutex_release(app->mutex); +} + +void build_alphabet(PassGen* app) { + PassGen_Alphabet mode = AlphabetLevels[app->level]; + app->alphabet[0] = '\0'; + if((mode & Digits) != 0) strcat(app->alphabet, PASSGEN_DIGITS); + if((mode & Lowercase) != 0) strcat(app->alphabet, PASSGEN_LETTERS_LOW); + if((mode & Uppercase) != 0) strcat(app->alphabet, PASSGEN_LETTERS_UP); + if((mode & Special) != 0) strcat(app->alphabet, PASSGEN_SPECIAL); +} + +PassGen* state_init() { + PassGen* app = malloc(sizeof(PassGen)); + app->length = 8; + app->level = 2; + build_alphabet(app); + app->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + app->view_port = view_port_alloc(); + app->gui = furi_record_open(RECORD_GUI); + app->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + view_port_input_callback_set(app->view_port, input_callback, app); + view_port_draw_callback_set(app->view_port, render_callback, app); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + + app->notify = furi_record_open(RECORD_NOTIFICATION); + + return app; +} + +void generate(PassGen* app) { + int hi = strlen(app->alphabet); + for(int i = 0; i < app->length; i++) { + int x = rand() % hi; + app->password[i] = app->alphabet[x]; + } + app->password[app->length] = '\0'; +} + +void update_password(PassGen* app, bool vibro) { + generate(app); + + if(vibro) + notification_message(app->notify, &PassGen_Alert_vibro); + else + notification_message(app->notify, &sequence_blink_blue_100); + view_port_update(app->view_port); +} + +int32_t passgenapp(void) { + PassGen* app = state_init(); + generate(app); + + while(1) { + InputEvent input; + while(furi_message_queue_get(app->input_queue, &input, FuriWaitForever) == FuriStatusOk) { + furi_check(furi_mutex_acquire(app->mutex, FuriWaitForever) == FuriStatusOk); + + if(input.type == InputTypeShort) { + switch(input.key) { + case InputKeyBack: + furi_mutex_release(app->mutex); + state_free(app); + return 0; + case InputKeyDown: + if(app->level > 0) { + app->level--; + build_alphabet(app); + update_password(app, false); + } else + notification_message(app->notify, &sequence_blink_red_100); + break; + case InputKeyUp: + if(app->level < AlphabetLevelsCount - 1) { + app->level++; + build_alphabet(app); + update_password(app, false); + } else + notification_message(app->notify, &sequence_blink_red_100); + break; + case InputKeyLeft: + if(app->length > 1) { + app->length--; + update_password(app, false); + } else + notification_message(app->notify, &sequence_blink_red_100); + break; + case InputKeyRight: + if(app->length < PASSGEN_MAX_LENGTH) { + app->length++; + update_password(app, false); + } else + notification_message(app->notify, &sequence_blink_red_100); + break; + case InputKeyOk: + update_password(app, true); + break; + default: + break; + } + } + furi_mutex_release(app->mutex); + } + } + state_free(app); + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/README.md new file mode 100644 index 000000000..b9e20fc0d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/README.md @@ -0,0 +1,23 @@ +# flipperzero-pong +A Pong game for the Flipper Zero + +![](https://github.com/nmrr/flipperzero-pong/blob/main/img/Flipper_Zero.jpg) + +Assuming the toolchain is already installed, copy **flipper_pong** directory to **applications_user** + +Plug your **Flipper Zero** and build the Pong : +``` +./fbt DEBUG=no LIB_DEBUG=no COMPACT=yes launch_app APPSRC=applications_user/flipper_pong +``` + +The game will automatically be launched after compilation + +Because of the position of control pad on the **Flipper Zero**, this Pong has been programmed to be played in vertical position + +Press **Up** or **Down** to move your paddle. Press back button to quit + +If you don't want to build the game, just simply copy **flipper_pong.fap** on your **Flipper Zero** + +## Gallery ## + + diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/application.fam new file mode 100644 index 000000000..b4593c656 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/application.fam @@ -0,0 +1,13 @@ +App( + appid="flipper_pong", + name="Pong", + apptype=FlipperAppType.EXTERNAL, + entry_point="flipper_pong_app", + cdefines=["APP_FLIPPER_PONG"], + requires=[ + "gui", + ], + stack_size=1 * 1024, + fap_icon="pong.png", + fap_category="Games_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/flipper_pong.c b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/flipper_pong.c new file mode 100644 index 000000000..b9abe4c1e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/flipper_pong.c @@ -0,0 +1,293 @@ +// CC0 1.0 Universal (CC0 1.0) +// Public Domain Dedication +// https://github.com/nmrr + +#include +#include +#include +#include +#include +#include + +#define SCREEN_SIZE_X 128 +#define SCREEN_SIZE_Y 64 +#define FPS 20 + +#define PAD_SIZE_X 3 +#define PAD_SIZE_Y 8 +#define PLAYER1_PAD_SPEED 2 +#define PLAYER2_PAD_SPEED 2 +#define BALL_SIZE 4 + +typedef enum { + EventTypeInput, + ClockEventTypeTick, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} EventApp; + +typedef struct Players { + uint8_t player1_X, player1_Y, player2_X, player2_Y; + uint16_t player1_score, player2_score; + uint8_t ball_X, ball_Y, ball_X_speed, ball_Y_speed, ball_X_direction, ball_Y_direction; +} Players; + +static void draw_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + Players* playersMutex = (Players*)acquire_mutex_block((ValueMutex*)ctx); + + canvas_draw_frame(canvas, 0, 0, 128, 64); + canvas_draw_box( + canvas, playersMutex->player1_X, playersMutex->player1_Y, PAD_SIZE_X, PAD_SIZE_Y); + canvas_draw_box( + canvas, playersMutex->player2_X, playersMutex->player2_Y, PAD_SIZE_X, PAD_SIZE_Y); + canvas_draw_box(canvas, playersMutex->ball_X, playersMutex->ball_Y, BALL_SIZE, BALL_SIZE); + + canvas_set_font(canvas, FontPrimary); + canvas_set_font_direction(canvas, CanvasDirectionBottomToTop); + char buffer[16]; + snprintf( + buffer, + sizeof(buffer), + "%u - %u", + playersMutex->player1_score, + playersMutex->player2_score); + canvas_draw_str_aligned( + canvas, SCREEN_SIZE_X / 2 + 15, SCREEN_SIZE_Y / 2 + 2, AlignCenter, AlignTop, buffer); + + release_mutex((ValueMutex*)ctx, playersMutex); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + EventApp event = {.type = EventTypeInput, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void clock_tick(void* ctx) { + furi_assert(ctx); + FuriMessageQueue* queue = ctx; + EventApp event = {.type = ClockEventTypeTick}; + furi_message_queue_put(queue, &event, 0); +} + +bool insidePad(uint8_t x, uint8_t y, uint8_t playerX, uint8_t playerY) { + if(x >= playerX && x <= playerX + PAD_SIZE_X && y >= playerY && y <= playerY + PAD_SIZE_Y) + return true; + return false; +} + +uint8_t changeSpeed() { + uint8_t randomuint8[1]; + while(1) { + furi_hal_random_fill_buf(randomuint8, 1); + randomuint8[0] &= 0b00000011; + if(randomuint8[0] >= 1) break; + } + return randomuint8[0]; +} + +uint8_t changeDirection() { + uint8_t randomuint8[1]; + furi_hal_random_fill_buf(randomuint8, 1); + randomuint8[0] &= 0b1; + return randomuint8[0]; +} + +int32_t flipper_pong_app() { + EventApp event; + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); + + Players players; + players.player1_X = SCREEN_SIZE_X - PAD_SIZE_X - 1; + players.player1_Y = SCREEN_SIZE_Y / 2 - PAD_SIZE_Y / 2; + players.player1_score = 0; + + players.player2_X = 1; + players.player2_Y = SCREEN_SIZE_Y / 2 - PAD_SIZE_Y / 2; + players.player2_score = 0; + + players.ball_X = SCREEN_SIZE_X / 2 - BALL_SIZE / 2; + players.ball_Y = SCREEN_SIZE_Y / 2 - BALL_SIZE / 2; + players.ball_X_speed = 1; + players.ball_Y_speed = 1; + players.ball_X_direction = changeDirection(); + players.ball_Y_direction = changeDirection(); + + ValueMutex state_mutex; + init_mutex(&state_mutex, &players, sizeof(Players)); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, draw_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + if(players.ball_X_direction == 0) + notification_message(notification, &sequence_set_only_red_255); + else + notification_message(notification, &sequence_set_only_blue_255); + + FuriTimer* timer = furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, 1000 / FPS); + + while(1) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); + Players* playersMutex = (Players*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + if(event.type == EventTypeInput) { + if(event.input.key == InputKeyBack) { + release_mutex(&state_mutex, playersMutex); + notification_message(notification, &sequence_set_only_green_255); + break; + } else if(event.input.key == InputKeyUp) { + if(playersMutex->player1_Y >= 1 + PLAYER1_PAD_SPEED) + playersMutex->player1_Y -= PLAYER1_PAD_SPEED; + else + playersMutex->player1_Y = 1; + } else if(event.input.key == InputKeyDown) { + if(playersMutex->player1_Y <= + SCREEN_SIZE_Y - PAD_SIZE_Y - PLAYER1_PAD_SPEED - 1) + playersMutex->player1_Y += PLAYER1_PAD_SPEED; + else + playersMutex->player1_Y = SCREEN_SIZE_Y - PAD_SIZE_Y - 1; + } + } else if(event.type == ClockEventTypeTick) { + if(playersMutex->ball_X + BALL_SIZE / 2 <= SCREEN_SIZE_X * 0.35 && + playersMutex->ball_X_direction == 0) { + if(playersMutex->ball_Y + BALL_SIZE / 2 < + playersMutex->player2_Y + PAD_SIZE_Y / 2) { + if(playersMutex->player2_Y >= 1 + PLAYER2_PAD_SPEED) + playersMutex->player2_Y -= PLAYER2_PAD_SPEED; + else + playersMutex->player2_Y = 1; + } else if( + playersMutex->ball_Y + BALL_SIZE / 2 > + playersMutex->player2_Y + PAD_SIZE_Y / 2) { + if(playersMutex->player2_Y <= + SCREEN_SIZE_Y - PAD_SIZE_Y - PLAYER2_PAD_SPEED - 1) + playersMutex->player2_Y += PLAYER2_PAD_SPEED; + else + playersMutex->player2_Y = SCREEN_SIZE_Y - PAD_SIZE_Y - 1; + } + } + + uint8_t ball_corner_X[4] = { + playersMutex->ball_X, + playersMutex->ball_X + BALL_SIZE, + playersMutex->ball_X + BALL_SIZE, + playersMutex->ball_X}; + uint8_t ball_corner_Y[4] = { + playersMutex->ball_Y, + playersMutex->ball_Y, + playersMutex->ball_Y + BALL_SIZE, + playersMutex->ball_Y + BALL_SIZE}; + bool insidePlayer1 = false, insidePlayer2 = false; + + for(int i = 0; i < 4; i++) { + if(insidePad( + ball_corner_X[i], + ball_corner_Y[i], + playersMutex->player1_X, + playersMutex->player1_Y) == true) { + insidePlayer1 = true; + break; + } + + if(insidePad( + ball_corner_X[i], + ball_corner_Y[i], + playersMutex->player2_X, + playersMutex->player2_Y) == true) { + insidePlayer2 = true; + break; + } + } + + if(insidePlayer1 == true) { + playersMutex->ball_X_direction = 0; + playersMutex->ball_X -= playersMutex->ball_X_speed; + playersMutex->ball_X_speed = changeSpeed(); + playersMutex->ball_Y_speed = changeSpeed(); + notification_message(notification, &sequence_set_only_red_255); + } else if(insidePlayer2 == true) { + playersMutex->ball_X_direction = 1; + playersMutex->ball_X += playersMutex->ball_X_speed; + playersMutex->ball_X_speed = changeSpeed(); + playersMutex->ball_Y_speed = changeSpeed(); + notification_message(notification, &sequence_set_only_blue_255); + } else { + if(playersMutex->ball_X_direction == 1) { + if(playersMutex->ball_X <= + SCREEN_SIZE_X - BALL_SIZE - 1 - playersMutex->ball_X_speed) { + playersMutex->ball_X += playersMutex->ball_X_speed; + } else { + playersMutex->ball_X = SCREEN_SIZE_X / 2 - BALL_SIZE / 2; + playersMutex->ball_Y = SCREEN_SIZE_Y / 2 - BALL_SIZE / 2; + playersMutex->ball_X_speed = 1; + playersMutex->ball_Y_speed = 1; + playersMutex->ball_X_direction = 0; + playersMutex->player2_score++; + notification_message(notification, &sequence_set_only_red_255); + } + } else { + if(playersMutex->ball_X >= 1 + playersMutex->ball_X_speed) { + playersMutex->ball_X -= playersMutex->ball_X_speed; + } else { + playersMutex->ball_X = SCREEN_SIZE_X / 2 - BALL_SIZE / 2; + playersMutex->ball_Y = SCREEN_SIZE_Y / 2 - BALL_SIZE / 2; + playersMutex->ball_X_speed = 1; + playersMutex->ball_Y_speed = 1; + playersMutex->ball_X_direction = 1; + playersMutex->player1_score++; + notification_message(notification, &sequence_set_only_blue_255); + } + } + } + + if(playersMutex->ball_Y_direction == 1) { + if(playersMutex->ball_Y <= + SCREEN_SIZE_Y - BALL_SIZE - 1 - playersMutex->ball_Y_speed) { + playersMutex->ball_Y += playersMutex->ball_Y_speed; + } else { + playersMutex->ball_Y = SCREEN_SIZE_Y - BALL_SIZE - 1; + playersMutex->ball_X_speed = changeSpeed(); + playersMutex->ball_Y_speed = changeSpeed(); + playersMutex->ball_Y_direction = 0; + } + } else { + if(playersMutex->ball_Y >= 1 + playersMutex->ball_Y_speed) { + playersMutex->ball_Y -= playersMutex->ball_Y_speed; + } else { + playersMutex->ball_Y = 1; + playersMutex->ball_X_speed = changeSpeed(); + playersMutex->ball_Y_speed = changeSpeed(); + playersMutex->ball_Y_direction = 1; + } + } + } + } + + release_mutex(&state_mutex, playersMutex); + view_port_update(view_port); + } + + notification_message(notification, &sequence_reset_rgb); + + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_timer_free(timer); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/img/Flipper_Zero.jpg b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/img/Flipper_Zero.jpg new file mode 100644 index 000000000..76774824d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/img/Flipper_Zero.jpg differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/img/flipper1.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/img/flipper1.png new file mode 100644 index 000000000..7876280eb Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/img/flipper1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/img/flipper2.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/img/flipper2.png new file mode 100644 index 000000000..63321cded Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/img/flipper2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/img/flipper3.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/img/flipper3.png new file mode 100644 index 000000000..fbabec8e2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/img/flipper3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/pong.png b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/pong.png new file mode 100644 index 000000000..507ce711c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipper_pong/pong.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/.github/workflows/release.yml b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/.github/workflows/release.yml new file mode 100644 index 000000000..6f6b08a43 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/.github/workflows/release.yml @@ -0,0 +1,30 @@ +name: Release + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: Checkout Flipper Zero Firmware + uses: actions/checkout@v3 + with: + repository: 'flipperdevices/flipperzero-firmware' + ref: '0.74.2' + submodules: true + - name: Checkout + uses: actions/checkout@v3 + with: + path: 'applications_user/qrcode_app' + - name: Build + run: ./fbt fap_qrcode + - name: Publish + uses: softprops/action-gh-release@v1 + with: + files: build/f7-firmware-D/.extapps/qrcode.fap + generate_release_notes: true + fail_on_unmatched_files: true diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/.gitignore b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/.gitignore new file mode 100644 index 000000000..c6127b38c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/LICENSE new file mode 100644 index 000000000..85e7f6b40 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Bob Matcuk + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/README.md new file mode 100644 index 000000000..3a08bff24 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/README.md @@ -0,0 +1,156 @@ +# flipperzero-qrcode +Display qrcodes on the [Flipper Zero] + +## Download +Grab the latest `qrcode.fap` from [Releases]. + +## Installation +Copy the `qrcode.fap` file onto your [Flipper Zero] sd card in the `apps/Tools` +directory. Then create a top level directory called `qrcodes` to store your +qrcode files. This can be done using [qFlipper], for example, by +draging-and-dropping `qrcode.fap` into `apps/Tools` and then navigating back to +the top level (where the directories like `infrared` and `nfc` live), right +click, and create a new folder called `qrcodes`. + +## Creating QR Codes +qrcode files are simple text files with the extension `.qrcode`. This app will +expect them to live in a top-level directory on your sd card called `qrcodes`. +They should have the following content: + +``` +Filetype: QRCode +Version: 0 +Message: your content here +``` + +### Message Format +qrcodes support 4 formats called "modes": numeric, alpha-numeric, binary, and +kanji. Because of the limited screen real-estate on the [Flipper Zero], you'll +want to pick the best mode for the data you are trying to display. + +The app will automatically detect the best mode to use, so the only thing you +need to do is make sure the message in your file is formatted to use the best +mode. For example, if your message is entirely numeric, make sure you don't +include any extraneous punctuation in your file. If you're only encoding a +domain name, make sure it's uppercase to take advantage of alpha-numeric mode, +etc. + +#### Numeric Mode +Consists of only numbers, nothing else. This mode can encode the most data. + +#### Alpha-Numeric Mode +This mode can encode numbers, uppercase letters *only*, spaces, and the +following symbols: `$%*+-./:`. This format _may_ be appropriate for urls, as +long as you're only encoding the domain name and you remember to use uppercase +letters (ex: `HTTP://EXAMPLE.COM`). If your url includes some path after the +domain, you'll likely need to use binary mode because the paths are usually +case-sensitive. + +A qrcode in alpha-numeric mode can encode ~40% less data than numeric mode. + +#### Binary Mode +This mode is a little bit of a misnomer: binary mode simply means that the +message will be encoded as 8-bit bytes. The qrcode standard stipulates that +text will use ISO-8859-1 (also known as Latin-1) encoding, _not_ utf8 as would +be the standard these days. However, _some_ readers _may_ automatically detect +utf8. To be standard-compliant, that basically means you can only use Latin +letters, numbers, and symbols. + +A qrcode in binary mode can encode ~60% less data than numeric mode, and ~30% +less than alpha-numeric. + +#### Kanji Mode +This mode is unsupported, so I won't go into detail. A limitation of the +underlying qrcode library that I'm using, unfortunately. If there's interest, +perhaps I'll hack in support sometime. + +## Using the App +The app is fairly straightforward. When it first starts, the file browser will +automatically open to the `qrcodes` directory and display any `.qrcode` files. +Select one using the arrow keys and the center button. The qrcode will display. +If you push the right arrow, some stats will display: the qrcode "Version" - +which corresponds to how big it is; the ECC level - which determines the +qrcode's resilience to damage, such as a dirty screen (Low, Medium, Quartile, +and High); and the qrcode Mode (Numeric, Alpha-Numeric, Binary, or Kanji). + +While viewing the stats, you can select Version or ECC using the up and down +arrows and the center button. You can then increase or decrease the Version or +ECC using up and down and save your choice using the center buttton. This +feature was mostly added for my own amusement and testing, but, theoretically, +it may help a reader that's having trouble if the default ECC is less than the +highest value ("H"): you can increase the Version by 1 and then set the ECC to +"H". Whether or not this helps depends on the reader. + +You can hide the stats by pressing the left arrow. + +When you're done viewing the qrcode, press the back button to return to the +file browser. If you push the back button in the file browser, the app will +exit. + +I will ask that you temper your expectations: the Flipper Zero screen is small +and many readers may have difficulty reading the qrcodes, especially if they +are encoding a lot of data. However, I have successfully got my iPhone to read +qrcodes encoding phone numbers, wifi info, and a url, all the way up to a +version 11 qrcode (ie, the largest size the screen will fit). + +## Example: Wifi QRCodes +Most phones can automatically connect to wifi networks from a qrcode. If you +should like to encode your wifi's connection info into a qrcode, here's how +you'd do it: + +``` +Filetype: QRCode +Version: 0 +Message: WIFI:S:;P:;T:; +``` + +Replace `` with the name of your wifi, `` with the password. +`` would be "WPA" or "WEP". If your wifi is open (no password), +this can be "None" and you can remove `P:;` from the message. If your +wifi is hidden (ie, does not broadcast the ssid), you can add `H:true;` to the +end. + +Note that if your ssid or password contain any of these characters: `\";,:`, +you'll need to "escape" it by placing a backslash (`\`) before it. + +For example, if my ssid was "wifiball" and not broadcast, and the password was +"pa$$:word" with WPA encryption, the message would be: + +``` +Message: WIFI:S:wifiball;P:pa$$\:word;T:WPA;H:true; +``` + +## Building +First, clone the [flipperzero-firmware] repo and then clone this repo in the +`applications_user` directory: + +```bash +git clone git@github.com:flipperdevices/flipperzero-firmware.git +cd flipperzero-firmware/applications_user +git clone git@github.com:bmatcuk/flipperzero-qrcode.git +``` + +Next, in the base of the [flipperzero-firmware] directory, run fbt: + +```bash +cd .. +./fbt fap_qrcode +``` + +This will automatically install dependencies and build the application. When it +has finished building, the .fap will be in +`build/f7-firmware-D/.extapps/qrcode.fap` (fbt output will tell you where to +find the .fap, should it change in the future). + +## qrcode library +This application uses the [QRCode] library by ricmoo. This is the same library +that is in the lib directory of the flipper-firmware repo (which was originally +included for a [now-removed demo app]), but modified slightly to fix some +compiler errors. + +[now-removed demo app]: https://github.com/flipperdevices/flipperzero-firmware/pull/160/files +[flipperzero-firmware]: https://github.com/flipperdevices/flipperzero-firmware +[Flipper Zero]: https://flipperzero.one/ +[QRCode]: https://github.com/ricmoo/QRCode +[qFlipper]: https://docs.flipperzero.one/qflipper +[Releases]: https://github.com/bmatcuk/flipperzero-qrcode/releases/latest diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/application.fam new file mode 100644 index 000000000..b3a67697e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/application.fam @@ -0,0 +1,19 @@ +App( + appid="qrcode", + name="QR Code", + fap_version=(1,1), + fap_description="Display qrcodes", + fap_author="Bob Matcuk", + fap_weburl="https://github.com/bmatcuk/flipperzero-qrcode", + apptype=FlipperAppType.EXTERNAL, + entry_point="qrcode_app", + stack_size=2 * 1024, + cdefines=["APP_QRCODE"], + requires=[ + "gui", + "dialogs", + ], + fap_category="Misc_Extra", + fap_icon="icons/qrcode_10px.png", + fap_icon_assets="icons", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/icons/qrcode_10px.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/icons/qrcode_10px.png new file mode 100644 index 000000000..cb31f0955 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/icons/qrcode_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/qrcode.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/qrcode.c new file mode 100644 index 000000000..c36285a17 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/qrcode.c @@ -0,0 +1,858 @@ +/** + * The MIT License (MIT) + * + * This library is written and maintained by Richard Moore. + * Major parts were derived from Project Nayuki's library. + * + * Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode) + * Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Special thanks to Nayuki (https://www.nayuki.io/) from which this library was + * heavily inspired and compared against. + * + * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp + */ + +#include "qrcode.h" + +#include +#include + +#if LOCK_VERSION == 0 + +static const uint16_t NUM_ERROR_CORRECTION_CODEWORDS[4][40] = { + // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + { 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium + { 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low + { 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High + { 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile +}; + +static const uint8_t NUM_ERROR_CORRECTION_BLOCKS[4][40] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + // 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + { 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + { 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + { 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High + { 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile +}; + +static const uint16_t NUM_RAW_DATA_MODULES[40] = { + // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 208, 359, 567, 807, 1079, 1383, 1568, 1936, 2336, 2768, 3232, 3728, 4256, 4651, 5243, 5867, 6523, + // 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 7211, 7931, 8683, 9252, 10068, 10916, 11796, 12708, 13652, 14628, 15371, 16411, 17483, 18587, + // 32, 33, 34, 35, 36, 37, 38, 39, 40 + 19723, 20891, 22091, 23008, 24272, 25568, 26896, 28256, 29648 +}; + +// @TODO: Put other LOCK_VERSIONS here +#elif LOCK_VERSION == 3 + +static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4] = { + 26, 15, 44, 36 +}; + +static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4] = { + 1, 1, 2, 2 +}; + +static const uint16_t NUM_RAW_DATA_MODULES = 567; + +#else + +#error Unsupported LOCK_VERSION (add it...) + +#endif + + +static int max(int a, int b) { + if (a > b) { return a; } + return b; +} + +/* +static int abs(int value) { + if (value < 0) { return -value; } + return value; +} +*/ + + +static int8_t getAlphanumeric(char c) { + + if (c >= '0' && c <= '9') { return (c - '0'); } + if (c >= 'A' && c <= 'Z') { return (c - 'A' + 10); } + + switch (c) { + case ' ': return 36; + case '$': return 37; + case '%': return 38; + case '*': return 39; + case '+': return 40; + case '-': return 41; + case '.': return 42; + case '/': return 43; + case ':': return 44; + } + + return -1; +} + +static bool isAlphanumeric(const char *text, uint16_t length) { + while (length != 0) { + if (getAlphanumeric(text[--length]) == -1) { return false; } + } + return true; +} + + +static bool isNumeric(const char *text, uint16_t length) { + while (length != 0) { + char c = text[--length]; + if (c < '0' || c > '9') { return false; } + } + return true; +} + + +// We store the following tightly packed (less 8) in modeInfo +// <=9 <=26 <= 40 +// NUMERIC ( 10, 12, 14); +// ALPHANUMERIC ( 9, 11, 13); +// BYTE ( 8, 16, 16); +static char getModeBits(uint8_t version, uint8_t mode) { + // Note: We use 15 instead of 16; since 15 doesn't exist and we cannot store 16 (8 + 8) in 3 bits + // hex(int("".join(reversed([('00' + bin(x - 8)[2:])[-3:] for x in [10, 9, 8, 12, 11, 15, 14, 13, 15]])), 2)) + unsigned int modeInfo = 0x7bbb80a; + +#if LOCK_VERSION == 0 || LOCK_VERSION > 9 + if (version > 9) { modeInfo >>= 9; } +#endif + +#if LOCK_VERSION == 0 || LOCK_VERSION > 26 + if (version > 26) { modeInfo >>= 9; } +#endif + + char result = 8 + ((modeInfo >> (3 * mode)) & 0x07); + if (result == 15) { result = 16; } + + return result; +} + + +typedef struct BitBucket { + uint32_t bitOffsetOrWidth; + uint16_t capacityBytes; + uint8_t *data; +} BitBucket; + +/* +void bb_dump(BitBucket *bitBuffer) { + printf("Buffer: "); + for (uint32_t i = 0; i < bitBuffer->capacityBytes; i++) { + printf("%02x", bitBuffer->data[i]); + if ((i % 4) == 3) { printf(" "); } + } + printf("\n"); +} +*/ + +static uint16_t bb_getGridSizeBytes(uint8_t size) { + return (((size * size) + 7) / 8); +} + +static uint16_t bb_getBufferSizeBytes(uint32_t bits) { + return ((bits + 7) / 8); +} + +static void bb_initBuffer(BitBucket *bitBuffer, uint8_t *data, int32_t capacityBytes) { + bitBuffer->bitOffsetOrWidth = 0; + bitBuffer->capacityBytes = capacityBytes; + bitBuffer->data = data; + + memset(data, 0, bitBuffer->capacityBytes); +} + +static void bb_initGrid(BitBucket *bitGrid, uint8_t *data, uint8_t size) { + bitGrid->bitOffsetOrWidth = size; + bitGrid->capacityBytes = bb_getGridSizeBytes(size); + bitGrid->data = data; + + memset(data, 0, bitGrid->capacityBytes); +} + +static void bb_appendBits(BitBucket *bitBuffer, uint32_t val, uint8_t length) { + uint32_t offset = bitBuffer->bitOffsetOrWidth; + for (int8_t i = length - 1; i >= 0; i--, offset++) { + bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7)); + } + bitBuffer->bitOffsetOrWidth = offset; +} +/* +void bb_setBits(BitBucket *bitBuffer, uint32_t val, int offset, uint8_t length) { + for (int8_t i = length - 1; i >= 0; i--, offset++) { + bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7)); + } +} +*/ +static void bb_setBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool on) { + uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; + uint8_t mask = 1 << (7 - (offset & 0x07)); + if (on) { + bitGrid->data[offset >> 3] |= mask; + } else { + bitGrid->data[offset >> 3] &= ~mask; + } +} + +static void bb_invertBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool invert) { + uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; + uint8_t mask = 1 << (7 - (offset & 0x07)); + bool on = ((bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0); + if (on ^ invert) { + bitGrid->data[offset >> 3] |= mask; + } else { + bitGrid->data[offset >> 3] &= ~mask; + } +} + +static bool bb_getBit(BitBucket *bitGrid, uint8_t x, uint8_t y) { + uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; + return (bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0; +} + + +// XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical +// properties, calling applyMask(m) twice with the same value is equivalent to no change at all. +// This means it is possible to apply a mask, undo it, and try another mask. Note that a final +// well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). +static void applyMask(BitBucket *modules, BitBucket *isFunction, uint8_t mask) { + uint8_t size = modules->bitOffsetOrWidth; + + for (uint8_t y = 0; y < size; y++) { + for (uint8_t x = 0; x < size; x++) { + if (bb_getBit(isFunction, x, y)) { continue; } + + bool invert = 0; + switch (mask) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + } + bb_invertBit(modules, x, y, invert); + } + } +} + +static void setFunctionModule(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y, bool on) { + bb_setBit(modules, x, y, on); + bb_setBit(isFunction, x, y, true); +} + +// Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). +static void drawFinderPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) { + uint8_t size = modules->bitOffsetOrWidth; + + for (int8_t i = -4; i <= 4; i++) { + for (int8_t j = -4; j <= 4; j++) { + uint8_t dist = max(abs(i), abs(j)); // Chebyshev/infinity norm + int16_t xx = x + j, yy = y + i; + if (0 <= xx && xx < size && 0 <= yy && yy < size) { + setFunctionModule(modules, isFunction, xx, yy, dist != 2 && dist != 4); + } + } + } +} + +// Draws a 5*5 alignment pattern, with the center module at (x, y). +static void drawAlignmentPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) { + for (int8_t i = -2; i <= 2; i++) { + for (int8_t j = -2; j <= 2; j++) { + setFunctionModule(modules, isFunction, x + j, y + i, max(abs(i), abs(j)) != 1); + } + } +} + +// Draws two copies of the format bits (with its own error correction code) +// based on the given mask and this object's error correction level field. +static void drawFormatBits(BitBucket *modules, BitBucket *isFunction, uint8_t ecc, uint8_t mask) { + + uint8_t size = modules->bitOffsetOrWidth; + + // Calculate error correction code and pack bits + uint32_t data = ecc << 3 | mask; // errCorrLvl is uint2, mask is uint3 + uint32_t rem = data; + for (int i = 0; i < 10; i++) { + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + } + + data = data << 10 | rem; + data ^= 0x5412; // uint15 + + // Draw first copy + for (uint8_t i = 0; i <= 5; i++) { + setFunctionModule(modules, isFunction, 8, i, ((data >> i) & 1) != 0); + } + + setFunctionModule(modules, isFunction, 8, 7, ((data >> 6) & 1) != 0); + setFunctionModule(modules, isFunction, 8, 8, ((data >> 7) & 1) != 0); + setFunctionModule(modules, isFunction, 7, 8, ((data >> 8) & 1) != 0); + + for (int8_t i = 9; i < 15; i++) { + setFunctionModule(modules, isFunction, 14 - i, 8, ((data >> i) & 1) != 0); + } + + // Draw second copy + for (int8_t i = 0; i <= 7; i++) { + setFunctionModule(modules, isFunction, size - 1 - i, 8, ((data >> i) & 1) != 0); + } + + for (int8_t i = 8; i < 15; i++) { + setFunctionModule(modules, isFunction, 8, size - 15 + i, ((data >> i) & 1) != 0); + } + + setFunctionModule(modules, isFunction, 8, size - 8, true); +} + + +// Draws two copies of the version bits (with its own error correction code), +// based on this object's version field (which only has an effect for 7 <= version <= 40). +static void drawVersion(BitBucket *modules, BitBucket *isFunction, uint8_t version) { + + int8_t size = modules->bitOffsetOrWidth; + +#if LOCK_VERSION != 0 && LOCK_VERSION < 7 + return; + +#else + if (version < 7) { return; } + + // Calculate error correction code and pack bits + uint32_t rem = version; // version is uint6, in the range [7, 40] + for (uint8_t i = 0; i < 12; i++) { + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + } + + uint32_t data = version << 12 | rem; // uint18 + + // Draw two copies + for (uint8_t i = 0; i < 18; i++) { + bool bit = ((data >> i) & 1) != 0; + uint8_t a = size - 11 + i % 3, b = i / 3; + setFunctionModule(modules, isFunction, a, b, bit); + setFunctionModule(modules, isFunction, b, a, bit); + } + +#endif +} + +static void drawFunctionPatterns(BitBucket *modules, BitBucket *isFunction, uint8_t version, uint8_t ecc) { + + uint8_t size = modules->bitOffsetOrWidth; + + // Draw the horizontal and vertical timing patterns + for (uint8_t i = 0; i < size; i++) { + setFunctionModule(modules, isFunction, 6, i, i % 2 == 0); + setFunctionModule(modules, isFunction, i, 6, i % 2 == 0); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + drawFinderPattern(modules, isFunction, 3, 3); + drawFinderPattern(modules, isFunction, size - 4, 3); + drawFinderPattern(modules, isFunction, 3, size - 4); + +#if LOCK_VERSION == 0 || LOCK_VERSION > 1 + + if (version > 1) { + + // Draw the numerous alignment patterns + + uint8_t alignCount = version / 7 + 2; + uint8_t step; + if (version != 32) { + step = (version * 4 + alignCount * 2 + 1) / (2 * alignCount - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2 + } else { // C-C-C-Combo breaker! + step = 26; + } + + uint8_t alignPositionIndex = alignCount - 1; + uint8_t alignPosition[alignCount]; + + alignPosition[0] = 6; + + uint8_t size = version * 4 + 17; + for (uint8_t i = 0, pos = size - 7; i < alignCount - 1; i++, pos -= step) { + alignPosition[alignPositionIndex--] = pos; + } + + for (uint8_t i = 0; i < alignCount; i++) { + for (uint8_t j = 0; j < alignCount; j++) { + if ((i == 0 && j == 0) || (i == 0 && j == alignCount - 1) || (i == alignCount - 1 && j == 0)) { + continue; // Skip the three finder corners + } else { + drawAlignmentPattern(modules, isFunction, alignPosition[i], alignPosition[j]); + } + } + } + } + +#endif + + // Draw configuration data + drawFormatBits(modules, isFunction, ecc, 0); // Dummy mask value; overwritten later in the constructor + drawVersion(modules, isFunction, version); +} + + +// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire +// data area of this QR Code symbol. Function modules need to be marked off before this is called. +static void drawCodewords(BitBucket *modules, BitBucket *isFunction, BitBucket *codewords) { + + uint32_t bitLength = codewords->bitOffsetOrWidth; + uint8_t *data = codewords->data; + + uint8_t size = modules->bitOffsetOrWidth; + + // Bit index into the data + uint32_t i = 0; + + // Do the funny zigzag scan + for (int16_t right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) { right = 5; } + + for (uint8_t vert = 0; vert < size; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + uint8_t x = right - j; // Actual x coordinate + bool upwards = ((right & 2) == 0) ^ (x < 6); + uint8_t y = upwards ? size - 1 - vert : vert; // Actual y coordinate + if (!bb_getBit(isFunction, x, y) && i < bitLength) { + bb_setBit(modules, x, y, ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0); + i++; + } + // If there are any remainder bits (0 to 7), they are already + // set to 0/false/white when the grid of modules was initialized + } + } + } +} + + + +#define PENALTY_N1 3 +#define PENALTY_N2 3 +#define PENALTY_N3 40 +#define PENALTY_N4 10 + +// Calculates and returns the penalty score based on state of this QR Code's current modules. +// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. +// @TODO: This can be optimized by working with the bytes instead of bits. +static uint32_t getPenaltyScore(BitBucket *modules) { + uint32_t result = 0; + + uint8_t size = modules->bitOffsetOrWidth; + + // Adjacent modules in row having same color + for (uint8_t y = 0; y < size; y++) { + + bool colorX = bb_getBit(modules, 0, y); + for (uint8_t x = 1, runX = 1; x < size; x++) { + bool cx = bb_getBit(modules, x, y); + if (cx != colorX) { + colorX = cx; + runX = 1; + + } else { + runX++; + if (runX == 5) { + result += PENALTY_N1; + } else if (runX > 5) { + result++; + } + } + } + } + + // Adjacent modules in column having same color + for (uint8_t x = 0; x < size; x++) { + bool colorY = bb_getBit(modules, x, 0); + for (uint8_t y = 1, runY = 1; y < size; y++) { + bool cy = bb_getBit(modules, x, y); + if (cy != colorY) { + colorY = cy; + runY = 1; + } else { + runY++; + if (runY == 5) { + result += PENALTY_N1; + } else if (runY > 5) { + result++; + } + } + } + } + + uint16_t black = 0; + for (uint8_t y = 0; y < size; y++) { + uint16_t bitsRow = 0, bitsCol = 0; + for (uint8_t x = 0; x < size; x++) { + bool color = bb_getBit(modules, x, y); + + // 2*2 blocks of modules having same color + if (x > 0 && y > 0) { + bool colorUL = bb_getBit(modules, x - 1, y - 1); + bool colorUR = bb_getBit(modules, x, y - 1); + bool colorL = bb_getBit(modules, x - 1, y); + if (color == colorUL && color == colorUR && color == colorL) { + result += PENALTY_N2; + } + } + + // Finder-like pattern in rows and columns + bitsRow = ((bitsRow << 1) & 0x7FF) | color; + bitsCol = ((bitsCol << 1) & 0x7FF) | bb_getBit(modules, y, x); + + // Needs 11 bits accumulated + if (x >= 10) { + if (bitsRow == 0x05D || bitsRow == 0x5D0) { + result += PENALTY_N3; + } + if (bitsCol == 0x05D || bitsCol == 0x5D0) { + result += PENALTY_N3; + } + } + + // Balance of black and white modules + if (color) { black++; } + } + } + + // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% + uint16_t total = size * size; + for (uint16_t k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++) { + result += PENALTY_N4; + } + + return result; +} + + +static uint8_t rs_multiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + // See: https://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication + uint16_t z = 0; + for (int8_t i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >> 7) * 0x11D); + z ^= ((y >> i) & 1) * x; + } + return z; +} + +static void rs_init(uint8_t degree, uint8_t *coeff) { + memset(coeff, 0, degree); + coeff[degree - 1] = 1; + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest term, and store the rest of the coefficients in order of descending powers. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint16_t root = 1; + for (uint8_t i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (uint8_t j = 0; j < degree; j++) { + coeff[j] = rs_multiply(coeff[j], root); + if (j + 1 < degree) { + coeff[j] ^= coeff[j + 1]; + } + } + root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D) + } +} + +static void rs_getRemainder(uint8_t degree, uint8_t *coeff, uint8_t *data, uint8_t length, uint8_t *result, uint8_t stride) { + // Compute the remainder by performing polynomial division + + //for (uint8_t i = 0; i < degree; i++) { result[] = 0; } + //memset(result, 0, degree); + + for (uint8_t i = 0; i < length; i++) { + uint8_t factor = data[i] ^ result[0]; + for (uint8_t j = 1; j < degree; j++) { + result[(j - 1) * stride] = result[j * stride]; + } + result[(degree - 1) * stride] = 0; + + for (uint8_t j = 0; j < degree; j++) { + result[j * stride] ^= rs_multiply(coeff[j], factor); + } + } +} + + + +static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, uint16_t length, uint8_t version) { + int8_t mode = MODE_BYTE; + + if (isNumeric((char*)text, length)) { + mode = MODE_NUMERIC; + bb_appendBits(dataCodewords, 1 << MODE_NUMERIC, 4); + bb_appendBits(dataCodewords, length, getModeBits(version, MODE_NUMERIC)); + + uint16_t accumData = 0; + uint8_t accumCount = 0; + for (uint16_t i = 0; i < length; i++) { + accumData = accumData * 10 + ((char)(text[i]) - '0'); + accumCount++; + if (accumCount == 3) { + bb_appendBits(dataCodewords, accumData, 10); + accumData = 0; + accumCount = 0; + } + } + + // 1 or 2 digits remaining + if (accumCount > 0) { + bb_appendBits(dataCodewords, accumData, accumCount * 3 + 1); + } + + } else if (isAlphanumeric((char*)text, length)) { + mode = MODE_ALPHANUMERIC; + bb_appendBits(dataCodewords, 1 << MODE_ALPHANUMERIC, 4); + bb_appendBits(dataCodewords, length, getModeBits(version, MODE_ALPHANUMERIC)); + + uint16_t accumData = 0; + uint8_t accumCount = 0; + for (uint16_t i = 0; i < length; i++) { + accumData = accumData * 45 + getAlphanumeric((char)(text[i])); + accumCount++; + if (accumCount == 2) { + bb_appendBits(dataCodewords, accumData, 11); + accumData = 0; + accumCount = 0; + } + } + + // 1 character remaining + if (accumCount > 0) { + bb_appendBits(dataCodewords, accumData, 6); + } + + } else { + bb_appendBits(dataCodewords, 1 << MODE_BYTE, 4); + bb_appendBits(dataCodewords, length, getModeBits(version, MODE_BYTE)); + for (uint16_t i = 0; i < length; i++) { + bb_appendBits(dataCodewords, (char)(text[i]), 8); + } + } + + //bb_setBits(dataCodewords, length, 4, getModeBits(version, mode)); + + return mode; +} + +static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data) { + + // See: http://www.thonky.com/qr-code-tutorial/structure-final-message + +#if LOCK_VERSION == 0 + uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc][version - 1]; + uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc][version - 1]; + uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1]; +#else + uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc]; + uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc]; + uint16_t moduleCount = NUM_RAW_DATA_MODULES; +#endif + + uint8_t blockEccLen = totalEcc / numBlocks; + uint8_t numShortBlocks = numBlocks - moduleCount / 8 % numBlocks; + uint8_t shortBlockLen = moduleCount / 8 / numBlocks; + + uint8_t shortDataBlockLen = shortBlockLen - blockEccLen; + + uint8_t result[data->capacityBytes]; + memset(result, 0, sizeof(result)); + + uint8_t coeff[blockEccLen]; + rs_init(blockEccLen, coeff); + + uint16_t offset = 0; + uint8_t *dataBytes = data->data; + + + // Interleave all short blocks + for (uint8_t i = 0; i < shortDataBlockLen; i++) { + uint16_t index = i; + uint8_t stride = shortDataBlockLen; + for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) { + result[offset++] = dataBytes[index]; + +#if LOCK_VERSION == 0 || LOCK_VERSION >= 5 + if (blockNum == numShortBlocks) { stride++; } +#endif + index += stride; + } + } + + // Version less than 5 only have short blocks +#if LOCK_VERSION == 0 || LOCK_VERSION >= 5 + { + // Interleave long blocks + uint16_t index = shortDataBlockLen * (numShortBlocks + 1); + uint8_t stride = shortDataBlockLen; + for (uint8_t blockNum = 0; blockNum < numBlocks - numShortBlocks; blockNum++) { + result[offset++] = dataBytes[index]; + + if (blockNum == 0) { stride++; } + index += stride; + } + } +#endif + + // Add all ecc blocks, interleaved + uint8_t blockSize = shortDataBlockLen; + for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) { + +#if LOCK_VERSION == 0 || LOCK_VERSION >= 5 + if (blockNum == numShortBlocks) { blockSize++; } +#endif + rs_getRemainder(blockEccLen, coeff, dataBytes, blockSize, &result[offset + blockNum], numBlocks); + dataBytes += blockSize; + } + + memcpy(data->data, result, data->capacityBytes); + data->bitOffsetOrWidth = moduleCount; +} + +// We store the Format bits tightly packed into a single byte (each of the 4 modes is 2 bits) +// The format bits can be determined by ECC_FORMAT_BITS >> (2 * ecc) +static const uint8_t ECC_FORMAT_BITS = (0x02 << 6) | (0x03 << 4) | (0x00 << 2) | (0x01 << 0); + + +uint16_t qrcode_getBufferSize(uint8_t version) { + return bb_getGridSizeBytes(4 * version + 17); +} + +// @TODO: Return error if data is too big. +int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length) { + uint8_t size = version * 4 + 17; + qrcode->version = version; + qrcode->size = size; + qrcode->ecc = ecc; + qrcode->modules = modules; + + uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03; + +#if LOCK_VERSION == 0 + uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1]; + uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1]; +#else + version = LOCK_VERSION; + uint16_t moduleCount = NUM_RAW_DATA_MODULES; + uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits]; +#endif + + struct BitBucket codewords; + uint8_t codewordBytes[bb_getBufferSizeBytes(moduleCount)]; + bb_initBuffer(&codewords, codewordBytes, (int32_t)sizeof(codewordBytes)); + + // Place the data code words into the buffer + int8_t mode = encodeDataCodewords(&codewords, data, length, version); + + if (mode < 0) { return -1; } + qrcode->mode = mode; + + // Add terminator and pad up to a byte if applicable + uint32_t padding = (dataCapacity * 8) - codewords.bitOffsetOrWidth; + if (padding > 4) { padding = 4; } + bb_appendBits(&codewords, 0, padding); + bb_appendBits(&codewords, 0, (8 - codewords.bitOffsetOrWidth % 8) % 8); + + // Pad with alternate bytes until data capacity is reached + for (uint8_t padByte = 0xEC; codewords.bitOffsetOrWidth < (dataCapacity * 8); padByte ^= 0xEC ^ 0x11) { + bb_appendBits(&codewords, padByte, 8); + } + + BitBucket modulesGrid; + bb_initGrid(&modulesGrid, modules, size); + + BitBucket isFunctionGrid; + uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)]; + bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size); + + // Draw function patterns, draw all codewords, do masking + drawFunctionPatterns(&modulesGrid, &isFunctionGrid, version, eccFormatBits); + performErrorCorrection(version, eccFormatBits, &codewords); + drawCodewords(&modulesGrid, &isFunctionGrid, &codewords); + + // Find the best (lowest penalty) mask + uint8_t mask = 0; + int32_t minPenalty = INT32_MAX; + for (uint8_t i = 0; i < 8; i++) { + drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, i); + applyMask(&modulesGrid, &isFunctionGrid, i); + int penalty = getPenaltyScore(&modulesGrid); + if (penalty < minPenalty) { + mask = i; + minPenalty = penalty; + } + applyMask(&modulesGrid, &isFunctionGrid, i); // Undoes the mask due to XOR + } + + qrcode->mask = mask; + + // Overwrite old format bits + drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, mask); + + // Apply the final choice of mask + applyMask(&modulesGrid, &isFunctionGrid, mask); + + return 0; +} + +int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data) { + return qrcode_initBytes(qrcode, modules, version, ecc, (uint8_t*)data, strlen(data)); +} + +bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y) { + if (x >= qrcode->size || y >= qrcode->size) { + return false; + } + + uint32_t offset = y * qrcode->size + x; + return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0; +} + +/* +uint8_t qrcode_getHexLength(QRCode *qrcode) { + return ((qrcode->size * qrcode->size) + 7) / 4; +} + +void qrcode_getHex(QRCode *qrcode, char *result) { + +} +*/ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/qrcode.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/qrcode.h new file mode 100644 index 000000000..6e8bf1c36 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/qrcode.h @@ -0,0 +1,100 @@ +/** + * The MIT License (MIT) + * + * This library is written and maintained by Richard Moore. + * Major parts were derived from Project Nayuki's library. + * + * Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode) + * Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Special thanks to Nayuki (https://www.nayuki.io/) from which this library was + * heavily inspired and compared against. + * + * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp + */ + + +#ifndef __QRCODE_H_ +#define __QRCODE_H_ + +// #ifndef __cplusplus +// typedef unsigned char bool; +// static const bool false = 0; +// static const bool true = 1; +// #endif + +#include +#include + + +// QR Code Format Encoding +#define MODE_NUMERIC 0 +#define MODE_ALPHANUMERIC 1 +#define MODE_BYTE 2 + + +// Error Correction Code Levels +#define ECC_LOW 0 +#define ECC_MEDIUM 1 +#define ECC_QUARTILE 2 +#define ECC_HIGH 3 + + +// If set to non-zero, this library can ONLY produce QR codes at that version +// This saves a lot of dynamic memory, as the codeword tables are skipped +#ifndef LOCK_VERSION +#define LOCK_VERSION 0 +#endif + + +typedef struct QRCode { + uint8_t version; + uint8_t size; + uint8_t ecc; + uint8_t mode; + uint8_t mask; + uint8_t *modules; +} QRCode; + + +#ifdef __cplusplus +extern "C"{ +#endif /* __cplusplus */ + + + +uint16_t qrcode_getBufferSize(uint8_t version); + +int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data); +int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length); + +bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y); + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __QRCODE_H_ */ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/qrcode_app.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/qrcode_app.c new file mode 100644 index 000000000..8a4d36783 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/qrcode_app.c @@ -0,0 +1,586 @@ +#include + +#include +#include +#include + +#include + +// this file is generated by the build script +#include +#include "qrcode.h" + +#define TAG "qrcode" +#define QRCODE_FOLDER ANY_PATH("qrcodes") +#define QRCODE_EXTENSION ".qrcode" +#define QRCODE_FILETYPE "QRCode" +#define QRCODE_FILE_VERSION 0 + +/** + * Maximum version is 11 because the f0 screen is only 64 pixels high and + * version 12 is 65x65. Version 11 is 61x61. + */ +#define MAX_QRCODE_VERSION 11 + +/** Maximum length by mode, ecc, and version */ +static const uint16_t MAX_LENGTH[3][4][MAX_QRCODE_VERSION] = { + { + // Numeric + {41, 77, 127, 187, 255, 322, 370, 461, 552, 652, 772}, // Low + {34, 63, 101, 149, 202, 255, 293, 365, 432, 513, 604}, // Medium + {27, 48, 77, 111, 144, 178, 207, 259, 312, 364, 427}, // Quartile + {17, 34, 58, 82, 106, 139, 154, 202, 235, 288, 331}, // High + }, + { + // Alphanumeric + {25, 47, 77, 114, 154, 195, 224, 279, 335, 395, 468}, // Low + {20, 38, 61, 90, 122, 154, 178, 221, 262, 311, 366}, // Medium + {16, 29, 47, 67, 87, 108, 125, 157, 189, 221, 259}, // Quartile + {10, 20, 35, 50, 64, 84, 93, 122, 143, 174, 200}, // High + }, + { + // Binary + {17, 32, 53, 78, 106, 134, 154, 192, 230, 271, 321}, // Low + {14, 26, 42, 62, 84, 106, 122, 152, 180, 213, 251}, // Medium + {11, 20, 32, 46, 60, 74, 86, 108, 130, 151, 177}, // Quartile + {7, 14, 24, 34, 44, 58, 64, 84, 98, 119, 137}, // High + }, +}; + +/** Main app instance */ +typedef struct { + FuriMessageQueue* input_queue; + Gui* gui; + ViewPort* view_port; + + FuriMutex** mutex; + FuriString* message; + QRCode* qrcode; + uint8_t min_version; + uint8_t max_ecc_at_min_version; + bool loading; + bool too_long; + bool show_stats; + uint8_t selected_idx; + bool edit; + uint8_t set_version; + uint8_t set_ecc; +} QRCodeApp; + +/** + * @param ecc ECC number + * @returns a character corresponding to the ecc level + */ +static char get_ecc_char(uint8_t ecc) { + switch (ecc) { + case 0: return 'L'; + case 1: return 'M'; + case 2: return 'Q'; + case 3: return 'H'; + default: return '?'; + } +} + +/** + * @param mode qrcode mode + * @returns a character corresponding to the mode + */ +static char get_mode_char(uint8_t mode) { + switch (mode) { + case 0: return 'N'; + case 1: return 'A'; + case 2: return 'B'; + case 3: return 'K'; + default: return '?'; + } +} + +/** + * Render + * @param canvas The canvas to render to + * @param ctx Context provided to the callback by view_port_draw_callback_set + */ +static void render_callback(Canvas* canvas, void* ctx) { + furi_assert(canvas); + furi_assert(ctx); + + QRCodeApp* instance = ctx; + furi_check(furi_mutex_acquire(instance->mutex, FuriWaitForever) == FuriStatusOk); + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + + uint8_t font_height = canvas_current_font_height(canvas); + uint8_t width = canvas_width(canvas); + uint8_t height = canvas_height(canvas); + if (instance->loading) { + canvas_draw_str_aligned(canvas, width / 2, height / 2, AlignCenter, AlignCenter, "Loading..."); + } else if (instance->qrcode) { + uint8_t size = instance->qrcode->size; + uint8_t pixel_size = height / size; + uint8_t top = (height - pixel_size * size) / 2; + uint8_t left = ((instance->show_stats ? 65 : width) - pixel_size * size) / 2; + for (uint8_t y = 0; y < size; y++) { + for (uint8_t x = 0; x < size; x++) { + if (qrcode_getModule(instance->qrcode, x, y)) { + if (pixel_size == 1) { + canvas_draw_dot(canvas, left + x * pixel_size, top + y * pixel_size); + } else { + canvas_draw_box(canvas, left + x * pixel_size, top + y * pixel_size, pixel_size, pixel_size); + } + } + } + } + + if (instance->show_stats) { + top = 10; + left = 66; + + FuriString* str = furi_string_alloc(); + + if (!instance->edit || instance->selected_idx == 0) { + furi_string_printf(str, "Ver: %i", instance->set_version); + canvas_draw_str(canvas, left + 5, top + font_height, furi_string_get_cstr(str)); + if (instance->selected_idx == 0) { + canvas_draw_triangle(canvas, left, top + font_height / 2, font_height - 4, 4, CanvasDirectionLeftToRight); + } + if (instance->edit) { + uint8_t arrow_left = left + 5 + canvas_string_width(canvas, "Ver: 8") / 2; + canvas_draw_triangle(canvas, arrow_left, top, font_height - 4, 4, CanvasDirectionBottomToTop); + canvas_draw_triangle(canvas, arrow_left, top + font_height + 1, font_height - 4, 4, CanvasDirectionTopToBottom); + } + } + + if (!instance->edit || instance->selected_idx == 1) { + furi_string_printf(str, "ECC: %c", get_ecc_char(instance->set_ecc)); + canvas_draw_str(canvas, left + 5, 2 * font_height + top + 2, furi_string_get_cstr(str)); + if (instance->selected_idx == 1) { + canvas_draw_triangle(canvas, left, 3 * font_height / 2 + top + 2, font_height - 4, 4, CanvasDirectionLeftToRight); + } + if (instance->edit) { + uint8_t arrow_left = left + 5 + canvas_string_width(canvas, "ECC: H") / 2; + canvas_draw_triangle(canvas, arrow_left, font_height + top + 2, font_height - 4, 4, CanvasDirectionBottomToTop); + canvas_draw_triangle(canvas, arrow_left, 2 * font_height + top + 3, font_height - 4, 4, CanvasDirectionTopToBottom); + } + } + + if (!instance->edit) { + furi_string_printf(str, "Mod: %c", get_mode_char(instance->qrcode->mode)); + canvas_draw_str(canvas, left + 5, 3 * font_height + top + 4, furi_string_get_cstr(str)); + } + + furi_string_free(str); + } + } else { + uint8_t margin = (height - font_height * 2) / 3; + canvas_draw_str_aligned(canvas, width / 2, margin, AlignCenter, AlignTop, "Could not load qrcode."); + if (instance->too_long) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, width / 2, margin * 2 + font_height, "Message is too long."); + } + } + + furi_mutex_release(instance->mutex); +} + +/** + * Handle input + * @param input_event The received input event + * @param ctx Context provided to the callback by view_port_input_callback_set + */ +static void input_callback(InputEvent* input_event, void* ctx) { + furi_assert(input_event); + furi_assert(ctx); + if (input_event->type == InputTypeShort) { + QRCodeApp* instance = ctx; + furi_message_queue_put(instance->input_queue, input_event, 0); + } +} + +/** + * Determine if the given string is all numeric + * @param str The string to test + * @returns true if the string is all numeric + */ +static bool is_numeric(const char* str, uint16_t len) { + furi_assert(str); + while (len > 0) { + char c = str[--len]; + if (c < '0' || c > '9') return false; + } + return true; +} + +/** + * Determine if the given string is alphanumeric + * @param str The string to test + * @returns true if the string is alphanumeric + */ +static bool is_alphanumeric(const char* str, uint16_t len) { + furi_assert(str); + while (len > 0) { + char c = str[--len]; + if (c >= '0' && c <= '9') continue; + if (c >= 'A' && c <= 'Z') continue; + if (c == ' ' + || c == '$' + || c == '%' + || c == '*' + || c == '+' + || c == '-' + || c == '.' + || c == '/' + || c == ':') + continue; + return false; + } + return true; +} + +/** + * Allocate a qrcode + * @param version qrcode version + * @returns an allocated QRCode + */ +static QRCode* qrcode_alloc(uint8_t version) { + QRCode* qrcode = malloc(sizeof(QRCode)); + qrcode->modules = malloc(qrcode_getBufferSize(version)); + return qrcode; +} + +/** + * Free a QRCode + * @param qrcode The QRCode to free + */ +static void qrcode_free(QRCode* qrcode) { + furi_assert(qrcode); + free(qrcode->modules); + free(qrcode); +} + +/** + * Rebuild the qrcode. Assumes that instance->message is the message to encode, + * that the mutex has been acquired, and the specified version/ecc will be + * sufficiently large enough to encode the full message. It is also assumed + * that the old qrcode will be free'd by the caller. + * @param instance The qrcode app instance + * @param version The qrcode version to use + * @param ecc The qrcode ECC level to use + * @returns true if the qrcode was successfully created + */ +static bool rebuild_qrcode(QRCodeApp* instance, uint8_t version, uint8_t ecc) { + furi_assert(instance); + furi_assert(instance->message); + + const char* cstr = furi_string_get_cstr(instance->message); + uint16_t len = strlen(cstr); + instance->qrcode = qrcode_alloc(version); + + int8_t res = qrcode_initBytes(instance->qrcode, instance->qrcode->modules, version, ecc, (uint8_t*)cstr, len); + if (res != 0) { + FURI_LOG_E(TAG, "Could not create qrcode"); + + qrcode_free(instance->qrcode); + instance->qrcode = NULL; + + return false; + } + return true; +} + +/** + * Load a qrcode from a string + * @param instance The qrcode app instance + * @param str The message to encode as a qrcode + * @returns true if the string was successfully loaded + */ +static bool qrcode_load_string(QRCodeApp* instance, FuriString* str) { + furi_assert(instance); + furi_assert(str); + + furi_check(furi_mutex_acquire(instance->mutex, FuriWaitForever) == FuriStatusOk); + if (instance->message) { + furi_string_free(instance->message); + instance->message = NULL; + } + if (instance->qrcode) { + qrcode_free(instance->qrcode); + instance->qrcode = NULL; + } + instance->too_long = false; + instance->show_stats = false; + instance->selected_idx = 0; + instance->edit = false; + + bool result = false; + do { + const char* cstr = furi_string_get_cstr(str); + uint16_t len = strlen(cstr); + + instance->message = furi_string_alloc_set(str); + if (!instance->message) { + FURI_LOG_E(TAG, "Could not allocate message"); + break; + } + + // figure out the qrcode "mode" + uint8_t mode = MODE_BYTE; + if (is_numeric(cstr, len)) mode = MODE_NUMERIC; + else if (is_alphanumeric(cstr, len)) mode = MODE_ALPHANUMERIC; + + // Figure out the smallest qrcode version that'll fit all of the data - + // we prefer the smallest version to maximize the pixel size of each + // module to improve reader performance. Here, version is the 0-based + // index. The qrcode_initBytes function will want a 1-based version + // number, so we'll add one later. + uint8_t ecc = ECC_LOW; + uint8_t version = 0; + while (version < MAX_QRCODE_VERSION && MAX_LENGTH[mode][ecc][version] < len) { + version++; + } + + if (version == MAX_QRCODE_VERSION) { + instance->too_long = true; + break; + } + + // Figure out the maximum ECC we can use. I shouldn't need to + // bounds-check ecc in this loop because I already know from the loop + // above that ECC_LOW (0) works... don't forget to add one to that + // version number... + ecc = ECC_HIGH; + while (MAX_LENGTH[mode][ecc][version] < len) { + ecc--; + } + version++; + + // Build the qrcode + if (!rebuild_qrcode(instance, version, ecc)) { + furi_string_free(instance->message); + instance->message = NULL; + break; + } + + instance->min_version = instance->set_version = version; + instance->max_ecc_at_min_version = instance->set_ecc = ecc; + result = true; + } while (false); + + instance->loading = false; + + furi_mutex_release(instance->mutex); + + return result; +} + +/** + * Load a qrcode from a file + * @param instance The qrcode app instance + * @param file_path Path to the file to read + * @returns true if the file was successfully loaded + */ +static bool qrcode_load_file(QRCodeApp* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + FuriString* temp_str = furi_string_alloc(); + bool result = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + do { + if (!flipper_format_file_open_existing(file, file_path)) break; + + uint32_t version = 0; + if (!flipper_format_read_header(file, temp_str, &version)) break; + if (furi_string_cmp_str(temp_str, QRCODE_FILETYPE) + || version != QRCODE_FILE_VERSION) { + FURI_LOG_E(TAG, "Incorrect file format or version"); + break; + } + + if (!flipper_format_read_string(file, "Message", temp_str)) { + FURI_LOG_E(TAG, "Message is missing"); + break; + } + + if (!qrcode_load_string(instance, temp_str)) { + break; + } + + result = true; + } while (false); + + furi_record_close(RECORD_STORAGE); + flipper_format_free(file); + furi_string_free(temp_str); + + return result; +} + +/** + * Allocate the qrcode app + * @returns a qrcode app instance + */ +static QRCodeApp* qrcode_app_alloc() { + QRCodeApp* instance = malloc(sizeof(QRCodeApp)); + + instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + instance->view_port = view_port_alloc(); + view_port_draw_callback_set(instance->view_port, render_callback, instance); + view_port_input_callback_set(instance->view_port, input_callback, instance); + + instance->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); + + instance->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + instance->message = NULL; + instance->qrcode = NULL; + instance->loading = true; + instance->too_long = false; + instance->show_stats = false; + instance->selected_idx = 0; + instance->edit = false; + + return instance; +} + +/** + * Free the qrcode app + * @param qrcode_app The app to free + */ +static void qrcode_app_free(QRCodeApp* instance) { + if (instance->message) furi_string_free(instance->message); + if (instance->qrcode) qrcode_free(instance->qrcode); + + gui_remove_view_port(instance->gui, instance->view_port); + furi_record_close(RECORD_GUI); + + view_port_free(instance->view_port); + + furi_message_queue_free(instance->input_queue); + + furi_mutex_free(instance->mutex); + + free(instance); +} + +/** App entrypoint */ +int32_t qrcode_app(void* p) { + QRCodeApp* instance = qrcode_app_alloc(); + FuriString* file_path = furi_string_alloc(); + + do { + if (p && strlen(p)) { + furi_string_set(file_path, (const char*)p); + } else { + furi_string_set(file_path, QRCODE_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, QRCODE_EXTENSION, &I_qrcode_10px); + browser_options.hide_ext = true; + browser_options.base_path = QRCODE_FOLDER; + + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options); + + furi_record_close(RECORD_DIALOGS); + if (!res) { + FURI_LOG_E(TAG, "No file selected"); + break; + } + } + + if (!qrcode_load_file(instance, furi_string_get_cstr(file_path))) { + FURI_LOG_E(TAG, "Unable to load file"); + } + + InputEvent input; + while (furi_message_queue_get(instance->input_queue, &input, FuriWaitForever) == FuriStatusOk) { + furi_check(furi_mutex_acquire(instance->mutex, FuriWaitForever) == FuriStatusOk); + + if (input.key == InputKeyBack) { + if (instance->message) { + furi_string_free(instance->message); + instance->message = NULL; + } + if (instance->qrcode) { + qrcode_free(instance->qrcode); + instance->qrcode = NULL; + } + instance->loading = true; + instance->edit = false; + furi_mutex_release(instance->mutex); + break; + } else if (input.key == InputKeyRight) { + instance->show_stats = true; + } else if (input.key == InputKeyLeft) { + instance->show_stats = false; + } else if (instance->show_stats && !instance->loading && instance->qrcode) { + if (input.key == InputKeyUp) { + if (!instance->edit) { + instance->selected_idx = MAX(0, instance->selected_idx - 1); + } else { + if (instance->selected_idx == 0 && instance->set_version < MAX_QRCODE_VERSION) { + instance->set_version++; + } else if (instance->selected_idx == 1) { + uint8_t max_ecc = instance->set_version == instance->min_version ? instance->max_ecc_at_min_version : ECC_HIGH; + if (instance->set_ecc < max_ecc) { + instance->set_ecc++; + } + } + } + } else if (input.key == InputKeyDown) { + if (!instance->edit) { + instance->selected_idx = MIN(1, instance->selected_idx + 1); + } else { + if (instance->selected_idx == 0 && instance->set_version > instance->min_version) { + instance->set_version--; + if (instance->set_version == instance->min_version) { + instance->set_ecc = MAX(instance->set_ecc, instance->max_ecc_at_min_version); + } + } else if (instance->selected_idx == 1 && instance->set_ecc > 0) { + instance->set_ecc--; + } + } + } else if (input.key == InputKeyOk) { + if (instance->edit && (instance->set_version != instance->qrcode->version || instance->set_ecc != instance->qrcode->ecc)) { + QRCode* qrcode = instance->qrcode; + instance->loading = true; + + if (rebuild_qrcode(instance, instance->set_version, instance->set_ecc)) { + qrcode_free(qrcode); + } else { + FURI_LOG_E(TAG, "Could not rebuild qrcode"); + instance->qrcode = qrcode; + instance->set_version = qrcode->version; + instance->set_ecc = qrcode->ecc; + } + + instance->loading = false; + } + instance->edit = !instance->edit; + } + } + + furi_mutex_release(instance->mutex); + view_port_update(instance->view_port); + } + + if (p && strlen(p)) { + // if started with an arg, exit instead + // of looping back to the browser + break; + } + } while (true); + + furi_string_free(file_path); + qrcode_app_free(instance); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/scripts/check-firmware.sh b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/scripts/check-firmware.sh new file mode 100644 index 000000000..15f3bc9e1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/scripts/check-firmware.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -Exeuo pipefail + +print_status() { + local level="$1" + local body="${2//%/%25}" + body="${body//$'\r'/}" + body="${body//$'\n'/%0A}" + + echo "::$level::$body" +} + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd -P)" +FIRMWARE_DIR="$1" + +LASTVER="$(git -C "$SCRIPT_DIR" tag | grep firmware-v | sort -V | tail -n1 | sed -e 's/^firmware-v//')" +print_status notice "last built against firmware version: $LASTVER" + +VER="$(git -C "$FIRMWARE_DIR" tag | sed -E -e '/^[0-9]+\.[0-9]+\.[0-9]+$/!d' | sort -V | sed -e "1,/$LASTVER/d" | tail -n1)" +# VER="$(curl https://api.github.com/repos/flipperdevices/flipperzero-firmware/tags | jq -r --arg current "$LASTVER" 'def ver($v): $v | ltrimstr("v") | split(".") | map(tonumber); map(.name) | map(select(. | test("^\\d+\\.\\d+\\.\\d+$";"s"))) | map(ver(.)) | map(select(. > ver($current))) | sort | last | if . == null then "" else join(".") end')" +if [ -z "$VER" ]; then + print_status notice "no new firmware version" + exit 0 +fi +print_status notice "new firmware version: $VER" +echo "version=$VER" >> $GITHUB_OUTPUT diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/scripts/update-firmware.sh b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/scripts/update-firmware.sh new file mode 100644 index 000000000..fadc99a4a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-qrcode/scripts/update-firmware.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -Exeuo pipefail + +print_status() { + local level="$1" + local body="${2//%/%25}" + body="${body//$'\r'/}" + body="${body//$'\n'/%0A}" + + echo "::$level::$body" +} + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd -P)" +FIRMWARE_VER="$1" + +pushd "$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel)" +print_status notice "updating to firmware $FIRMWARE_VER" + +# setup git +git config --local user.name $GIT_USER_NAME +git config --local user.email $GIT_USER_EMAIL + +# construct a new version number for the qrcode app +VER="$(git tag | sed -E -e '/^v[0-9]+\.[0-9]+\.[0-9]+$/!d' | sort -V | tail -n1)" +if [[ "$VER" =~ ^(v[0-9]+.[0-9]+).([0-9]+)$ ]]; then + VER="${BASH_REMATCH[1]}.$(( ${BASH_REMATCH[2]} + 1 ))" +else + print_status warning "couldn't construct new version number from $VER" + exit 1 +fi +print_status notice "new qrcode version: $VER" + +# update firmware version in automation +sed -i -e "/firmware_version:/s/'.*'/'$FIRMWARE_VER'/" .github/workflows/release.yml + +# commit and tag +git add .github/workflows/release.yml +git commit -m "update to firmware $FIRMWARE_VER" +git tag -a -m "$VER" "$VER" +git tag "firmware-v$FIRMWARE_VER" +git push --atomic origin main "$VER" "firmware-v$FIRMWARE_VER" + +popd diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/README.md new file mode 100644 index 000000000..5524eba3e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/README.md @@ -0,0 +1,30 @@ +# Tuning Fork + +Inspired by [Metronome](https://github.com/panki27/Metronome) + +A tuning fork for the [Flipper Zero](https://flipperzero.one/) device. +Allows to play different notes in different pitches. + +![screenshot](img/tuning_fork.gif) + +## Features +- Tuning forks (440Hz, 432Hz, etc.) +- Scientific pitch (..., 256Hz, 512Hz, 1024Hz, ...) +- Guitar Standard (6 strings) +- Guitar Drop D (6 strings) +- Guitar D (6 strings) +- Guitar Drop C (6 strings) +- Guitar Standard (7 strings) +- Bass Standard (4 strings) +- Bass Standard Tenor (4 strings) +- Bass Standard (5 strings) +- Bass Standard Tenor (5 strings) +- Bass Drop D (4 strings) +- Bass D (4 strings) +- Bass Drop A (5 strings) + +## Compiling + +``` +./fbt firmware_tuning_fork +``` diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/application.fam new file mode 100644 index 000000000..f45f93b12 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/application.fam @@ -0,0 +1,14 @@ +App( + appid="tuning_fork", + name="Tuning Fork", + apptype=FlipperAppType.EXTERNAL, + entry_point="tuning_fork_app", + cdefines=["APP_TUNING_FORM"], + requires=[ + "gui", + ], + fap_icon="tuning_fork_icon.png", + fap_category="Music_Extra", + stack_size=2 * 1024, + order=20, +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/img/screenshot_1.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/img/screenshot_1.png new file mode 100644 index 000000000..047279889 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/img/screenshot_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/img/screenshot_2.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/img/screenshot_2.png new file mode 100644 index 000000000..c31f37744 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/img/screenshot_2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/img/tuning_fork.gif b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/img/tuning_fork.gif new file mode 100644 index 000000000..27bfe8cbe Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/img/tuning_fork.gif differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/notes.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/notes.h new file mode 100644 index 000000000..c00b4f8ed --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/notes.h @@ -0,0 +1,158 @@ +#ifndef NOTES +#define NOTES + +#define C0 16.35f +#define Cs0 17.32f +#define Db0 17.32f +#define D0 18.35f +#define Ds0 19.45f +#define Eb0 19.45f +#define E0 20.60f +#define F0 21.83f +#define Fs0 23.12f +#define Gb0 23.12f +#define G0 24.50f +#define Gs0 25.96f +#define Ab0 25.96f +#define A0 27.50f +#define As0 29.14f +#define Bb0 29.14f +#define B0 30.868f +#define C1 32.70f +#define Cs1 34.65f +#define Db1 34.65f +#define D1 36.71f +#define Ds1 38.89f +#define Eb1 38.89f +#define E1 41.203f +#define F1 43.65f +#define Fs1 46.25f +#define Gb1 46.25f +#define G1 49.00f +#define Gs1 51.91f +#define Ab1 51.91f +#define A1 55.00f +#define As1 58.27f +#define Bb1 58.27f +#define B1 61.74f +#define C2 65.41f +#define Cs2 69.30f +#define Db2 69.30f +#define D2 73.416f +#define Ds2 77.78f +#define Eb2 77.78f +#define E2 82.41f +#define F2 87.31f +#define Fs2 92.50f +#define Gb2 92.50f +#define G2 97.999f +#define Gs2 103.83f +#define Ab2 103.83f +#define A2 110.00f +#define As2 116.54f +#define Bb2 116.54f +#define B2 123.47f +#define C3 130.813f +#define Cs3 138.59f +#define Db3 138.59f +#define D3 146.83f +#define Ds3 155.56f +#define Eb3 155.56f +#define E3 164.81f +#define F3 174.61f +#define Fs3 185.00f +#define Gb3 185.00f +#define G3 196.00f +#define Gs3 207.65f +#define Ab3 207.65f +#define A3 220.00f +#define As3 233.08f +#define Bb3 233.08f +#define B3 246.94f +#define C4 261.63f +#define Cs4 277.18f +#define Db4 277.18f +#define D4 293.66f +#define Ds4 311.13f +#define Eb4 311.13f +#define E4 329.63f +#define F4 349.23f +#define Fs4 369.99f +#define Gb4 369.99f +#define G4 392.00f +#define Gs4 415.30f +#define Ab4 415.30f +#define A4 440.00f +#define As4 466.16f +#define Bb4 466.16f +#define B4 493.88f +#define C5 523.25f +#define Cs5 554.37f +#define Db5 554.37f +#define D5 587.33f +#define Ds5 622.25f +#define Eb5 622.25f +#define E5 659.25f +#define F5 698.46f +#define Fs5 739.99f +#define Gb5 739.99f +#define G5 783.99f +#define Gs5 830.61f +#define Ab5 830.61f +#define A5 880.00f +#define As5 932.33f +#define Bb5 932.33f +#define B5 987.77f +#define C6 1046.50f +#define Cs6 1108.73f +#define Db6 1108.73f +#define D6 1174.66f +#define Ds6 1244.51f +#define Eb6 1244.51f +#define E6 1318.51f +#define F6 1396.91f +#define Fs6 1479.98f +#define Gb6 1479.98f +#define G6 1567.98f +#define Gs6 1661.22f +#define Ab6 1661.22f +#define A6 1760.00f +#define As6 1864.66f +#define Bb6 1864.66f +#define B6 1975.53f +#define C7 2093.00f +#define Cs7 2217.46f +#define Db7 2217.46f +#define D7 2349.32f +#define Ds7 2489.02f +#define Eb7 2489.02f +#define E7 2637.02f +#define F7 2793.83f +#define Fs7 2959.96f +#define Gb7 2959.96f +#define G7 3135.96f +#define Gs7 3322.44f +#define Ab7 3322.44f +#define A7 3520.00f +#define As7 3729.31f +#define Bb7 3729.31f +#define B7 3951.07f +#define C8 4186.01f +#define Cs8 4434.92f +#define Db8 4434.92f +#define D8 4698.63f +#define Ds8 4978.03f +#define Eb8 4978.03f +#define E8 5274.04f +#define F8 5587.65f +#define Fs8 5919.91f +#define Gb8 5919.91f +#define G8 6271.93f +#define Gs8 6644.88f +#define Ab8 6644.88f +#define A8 7040.00f +#define As8 7458.62f +#define Bb8 7458.62f +#define B8 7902.13f + +#endif //NOTES diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/tuning_fork.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/tuning_fork.c new file mode 100644 index 000000000..b2bc6fb96 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/tuning_fork.c @@ -0,0 +1,408 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "notes.h" +#include "tunings.h" + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +enum Page { Tunings, Notes }; + +typedef struct { + bool playing; + enum Page page; + int current_tuning_note_index; + int current_tuning_index; + float volume; + TUNING tuning; +} TuningForkState; + +static TUNING current_tuning(TuningForkState* tuningForkState) { + return tuningForkState->tuning; +} + +static NOTE current_tuning_note(TuningForkState* tuningForkState) { + return current_tuning(tuningForkState).notes[tuningForkState->current_tuning_note_index]; +} + +static float current_tuning_note_freq(TuningForkState* tuningForkState) { + return current_tuning_note(tuningForkState).frequency; +} + +static void current_tuning_note_label(TuningForkState* tuningForkState, char* outNoteLabel) { + for(int i = 0; i < 20; ++i) { + outNoteLabel[i] = current_tuning_note(tuningForkState).label[i]; + } +} + +static void current_tuning_label(TuningForkState* tuningForkState, char* outTuningLabel) { + for(int i = 0; i < 20; ++i) { + outTuningLabel[i] = current_tuning(tuningForkState).label[i]; + } +} + +static void updateTuning(TuningForkState* tuning_fork_state) { + tuning_fork_state->tuning = TuningList[tuning_fork_state->current_tuning_index]; + tuning_fork_state->current_tuning_note_index = 0; +} + +static void next_tuning(TuningForkState* tuning_fork_state) { + if(tuning_fork_state->current_tuning_index == TUNINGS_COUNT - 1) { + tuning_fork_state->current_tuning_index = 0; + } else { + tuning_fork_state->current_tuning_index += 1; + } + updateTuning(tuning_fork_state); +} + +static void prev_tuning(TuningForkState* tuning_fork_state) { + if(tuning_fork_state->current_tuning_index - 1 < 0) { + tuning_fork_state->current_tuning_index = TUNINGS_COUNT - 1; + } else { + tuning_fork_state->current_tuning_index -= 1; + } + updateTuning(tuning_fork_state); +} + +static void next_note(TuningForkState* tuning_fork_state) { + if(tuning_fork_state->current_tuning_note_index == + current_tuning(tuning_fork_state).notes_length - 1) { + tuning_fork_state->current_tuning_note_index = 0; + } else { + tuning_fork_state->current_tuning_note_index += 1; + } +} + +static void prev_note(TuningForkState* tuning_fork_state) { + if(tuning_fork_state->current_tuning_note_index == 0) { + tuning_fork_state->current_tuning_note_index = + current_tuning(tuning_fork_state).notes_length - 1; + } else { + tuning_fork_state->current_tuning_note_index -= 1; + } +} + +static void increase_volume(TuningForkState* tuning_fork_state) { + if(tuning_fork_state->volume < 1.0f) { + tuning_fork_state->volume += 0.1f; + } +} + +static void decrease_volume(TuningForkState* tuning_fork_state) { + if(tuning_fork_state->volume > 0.0f) { + tuning_fork_state->volume -= 0.1f; + } +} + +static void play(TuningForkState* tuning_fork_state) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + furi_hal_speaker_start( + current_tuning_note_freq(tuning_fork_state), tuning_fork_state->volume); + } +} + +static void stop() { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } +} + +static void replay(TuningForkState* tuning_fork_state) { + stop(); + play(tuning_fork_state); +} + +static void render_callback(Canvas* const canvas, void* ctx) { + TuningForkState* tuning_fork_state = acquire_mutex((ValueMutex*)ctx, 25); + if(tuning_fork_state == NULL) { + return; + } + + string_t tempStr; + string_init(tempStr); + + canvas_draw_frame(canvas, 0, 0, 128, 64); + + canvas_set_font(canvas, FontPrimary); + + if(tuning_fork_state->page == Tunings) { + char tuningLabel[20]; + current_tuning_label(tuning_fork_state, tuningLabel); + string_printf(tempStr, "< %s >", tuningLabel); + canvas_draw_str_aligned( + canvas, 64, 28, AlignCenter, AlignCenter, string_get_cstr(tempStr)); + string_reset(tempStr); + } else { + char tuningLabel[20]; + current_tuning_label(tuning_fork_state, tuningLabel); + string_printf(tempStr, "%s", tuningLabel); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, string_get_cstr(tempStr)); + string_reset(tempStr); + + char tuningNoteLabel[20]; + current_tuning_note_label(tuning_fork_state, tuningNoteLabel); + string_printf(tempStr, "< %s >", tuningNoteLabel); + canvas_draw_str_aligned( + canvas, 64, 24, AlignCenter, AlignCenter, string_get_cstr(tempStr)); + string_reset(tempStr); + } + + canvas_set_font(canvas, FontSecondary); + elements_button_left(canvas, "Prev"); + elements_button_right(canvas, "Next"); + + if(tuning_fork_state->page == Notes) { + if(tuning_fork_state->playing) { + elements_button_center(canvas, "Stop "); + } else { + elements_button_center(canvas, "Play"); + } + } else { + elements_button_center(canvas, "Select"); + } + if(tuning_fork_state->page == Notes) { + elements_progress_bar(canvas, 8, 36, 112, tuning_fork_state->volume); + } + + string_clear(tempStr); + release_mutex((ValueMutex*)ctx, tuning_fork_state); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void tuning_fork_state_init(TuningForkState* const tuning_fork_state) { + tuning_fork_state->playing = false; + tuning_fork_state->page = Tunings; + tuning_fork_state->volume = 1.0f; + tuning_fork_state->tuning = GuitarStandard6; + tuning_fork_state->current_tuning_index = 2; + tuning_fork_state->current_tuning_note_index = 0; +} + +int32_t tuning_fork_app() { + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + TuningForkState* tuning_fork_state = malloc(sizeof(TuningForkState)); + tuning_fork_state_init(tuning_fork_state); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, tuning_fork_state, sizeof(TuningForkState))) { + FURI_LOG_E("TuningFork", "cannot create mutex\r\n"); + free(tuning_fork_state); + return 255; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + + TuningForkState* tuning_fork_state = (TuningForkState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeShort) { + // push events + switch(event.input.key) { + case InputKeyUp: + if(tuning_fork_state->page == Notes) { + increase_volume(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + break; + case InputKeyDown: + if(tuning_fork_state->page == Notes) { + decrease_volume(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + break; + case InputKeyRight: + if(tuning_fork_state->page == Tunings) { + next_tuning(tuning_fork_state); + } else { + next_note(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + break; + case InputKeyLeft: + if(tuning_fork_state->page == Tunings) { + prev_tuning(tuning_fork_state); + } else { + prev_note(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + break; + case InputKeyOk: + if(tuning_fork_state->page == Tunings) { + tuning_fork_state->page = Notes; + } else { + tuning_fork_state->playing = !tuning_fork_state->playing; + if(tuning_fork_state->playing) { + play(tuning_fork_state); + } else { + stop(); + } + } + break; + case InputKeyBack: + if(tuning_fork_state->page == Tunings) { + processing = false; + } else { + tuning_fork_state->playing = false; + tuning_fork_state->current_tuning_note_index = 0; + stop(); + tuning_fork_state->page = Tunings; + } + break; + default: + break; + } + } else if(event.input.type == InputTypeLong) { + // hold events + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + if(tuning_fork_state->page == Tunings) { + next_tuning(tuning_fork_state); + } else { + next_note(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + + break; + case InputKeyLeft: + if(tuning_fork_state->page == Tunings) { + prev_tuning(tuning_fork_state); + } else { + prev_note(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + + break; + case InputKeyOk: + break; + case InputKeyBack: + if(tuning_fork_state->page == Tunings) { + processing = false; + } else { + tuning_fork_state->playing = false; + stop(); + tuning_fork_state->page = Tunings; + tuning_fork_state->current_tuning_note_index = 0; + } + break; + default: + break; + } + } else if(event.input.type == InputTypeRepeat) { + // repeat events + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + if(tuning_fork_state->page == Tunings) { + next_tuning(tuning_fork_state); + } else { + next_note(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + + break; + case InputKeyLeft: + if(tuning_fork_state->page == Tunings) { + prev_tuning(tuning_fork_state); + } else { + prev_note(tuning_fork_state); + if(tuning_fork_state->playing) { + replay(tuning_fork_state); + } + } + + break; + case InputKeyOk: + break; + case InputKeyBack: + if(tuning_fork_state->page == Tunings) { + processing = false; + } else { + tuning_fork_state->playing = false; + stop(); + tuning_fork_state->page = Tunings; + tuning_fork_state->current_tuning_note_index = 0; + } + break; + default: + break; + } + } + } + } else { + FURI_LOG_D("TuningFork", "FuriMessageQueue: event timeout"); + } + + view_port_update(view_port); + release_mutex(&state_mutex, tuning_fork_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + furi_record_close(RECORD_NOTIFICATION); + free(tuning_fork_state); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/tuning_fork_icon.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/tuning_fork_icon.png new file mode 100644 index 000000000..074d9d590 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/tuning_fork_icon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/tunings.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/tunings.h new file mode 100644 index 000000000..14bf469fe --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-tuning-fork/tunings.h @@ -0,0 +1,151 @@ +#include "notes.h" + +#ifndef TUNINGS +#define TUNINGS + +typedef struct { + char label[20]; + float frequency; +} NOTE; + +typedef struct { + char label[20]; + int notes_length; + NOTE notes[20]; +} TUNING; + +const TUNING TuningForks = { + "Tuning forks", + 6, + { + {"Common A4 (440)", 440.00f}, + {"Sarti's A4 (436)", 436.00f}, + {"1858 A4 (435)", 435.00f}, + {"Verdi's A4 (432)", 432.00f}, + {"1750-1820 A4 (423.5)", 423.50f}, + {"Verdi's C4 (256.00)", 256.00f}, + }}; + +const TUNING ScientificPitch = { + "Scientific pitch", + 12, + {{"C0 (16Hz)", 16.0f}, + {"C1 (32Hz)", 32.0f}, + {"C2 (64Hz)", 64.0f}, + {"C3 (128Hz)", 128.0f}, + {"C4 (256Hz)", 256.0f}, + {"C5 (512Hz)", 512.0f}, + {"C6 (1024Hz)", 1024.0f}, + {"C7 (2048Hz)", 2048.0f}, + {"C8 (4096Hz)", 4096.0f}, + {"C9 (8192Hz)", 8192.0f}, + {"C10 (16384Hz)", 16384.0f}, + {"C11 (32768Hz)", 32768.0f}}}; + +const TUNING GuitarStandard6 = { + "Guitar Standard 6", + 6, + {{"String 1", E4}, + {"String 2", B3}, + {"String 3", G3}, + {"String 4", D3}, + {"String 5", A2}, + {"String 6", E2}}}; + +const TUNING GuitarDropD6 = { + "Guitar Drop D 6", + 6, + {{"String 1", E4}, + {"String 2", B3}, + {"String 3", G3}, + {"String 4", D3}, + {"String 5", A2}, + {"String 6", D2}}}; + +const TUNING GuitarD6 = { + "Guitar D 6", + 6, + {{"String 1", D4}, + {"String 2", A3}, + {"String 3", F3}, + {"String 4", C3}, + {"String 5", G2}, + {"String 6", D2}}}; + +const TUNING GuitarDropC6 = { + "Guitar Drop C 6", + 6, + {{"String 1", D4}, + {"String 2", A3}, + {"String 3", F3}, + {"String 4", C3}, + {"String 5", G2}, + {"String 6", C2}}}; + +const TUNING GuitarStandard7 = { + "Guitar Standard 7", + 7, + {{"String 1", E4}, + {"String 2", B3}, + {"String 3", G3}, + {"String 4", D3}, + {"String 5", A2}, + {"String 6", E2}, + {"String 7", B1}}}; + +const TUNING BassStandard4 = { + "Bass Standard 4", + 4, + {{"String 1", G2}, {"String 2", D2}, {"String 3", A1}, {"String 4", E1}}}; + +const TUNING BassStandardTenor4 = { + "Bass Stand Tenor 4", + 4, + {{"String 1", C3}, {"String 2", G2}, {"String 3", D2}, {"String 4", A1}}}; + +const TUNING BassStandard5 = { + "Bass Standard 5", + 5, + {{"String 1", G2}, {"String 2", D2}, {"String 3", A1}, {"String 4", E1}, {"String 5", B0}}}; + +const TUNING BassStandardTenor5 = { + "Bass Stand Tenor 5", + 5, + {{"String 1", C3}, {"String 2", G2}, {"String 3", D2}, {"String 4", A1}, {"String 5", E1}}}; + +const TUNING BassDropD4 = { + "Bass Drop D 4", + 4, + {{"String 1", G2}, {"String 2", D2}, {"String 3", A1}, {"String 4", D1}}}; + +const TUNING BassD4 = { + "Bass D 4", + 4, + {{"String 1", F2}, {"String 2", C2}, {"String 3", G1}, {"String 4", D1}}}; + +const TUNING BassDropA5 = { + "Bass Drop A 5", + 5, + {{"String 1", G2}, {"String 2", D2}, {"String 3", A1}, {"String 4", E1}, {"String 5", A0}}}; + +#define TUNINGS_COUNT 14 + +TUNING TuningList[TUNINGS_COUNT] = { + ScientificPitch, + TuningForks, + + GuitarStandard6, + GuitarDropD6, + GuitarD6, + GuitarDropC6, + GuitarStandard7, + + BassStandard4, + BassStandardTenor4, + BassStandard5, + BassStandardTenor5, + BassDropD4, + BassD4, + BassDropA5}; + +#endif //TUNINGS diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/.gitignore b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/.gitignore new file mode 100644 index 000000000..c6127b38c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/README.md new file mode 100644 index 000000000..25351ef49 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/README.md @@ -0,0 +1,48 @@ +# flipperzero-yatzee +Yahtzee game for flipperzero + +Its not beautiful, but it works and now I can play Yahtzee on my flipper while I shit at work. + +Installation: + +Download fap from releases. Copy fap to flipper sd at ext > apps > Games + + +Controls: + +- Up to Roll +- Left/Right to move cursor +- OK to Hold a die +- Moving cursor past the dice will move the cursor up to the scorecard. Moving the scores cursor will show you the potential score you would get. + + +Rules & Scoring: + +- Between rolls, move the cursor and use the OK button to select which dice you will hold for the next roll +- 3 rolls per round and then you are forced to select a score. +- To score, move cursor with Left/Right up to the scorecard, when desired score to count is underlined, press the Down button to confirm. + +- 1-6 add up the corresponding dice of that number in your roll. +- 3 of a Kind (3k) = total of dice when 3 of a kind is rolled +- 4 of a Kind (4k) = total of dice when 4 of a kind is rolled +- Full House (Fh) = 25 +- Small Straight (Sm) = 30 +- Large Straight (Lg) = 40 +- Chance (Ch) = total of all dice in roll +- Yatzee (Yz) = 50 for the first yatzee. Successive Yatzees do not show in the score card, but add 100 each to the total score +- Game ends when every scoring value has been selected once. +- If sub score is at least 63, 35 points are added to the total score. + +Todo +- Redo the scorecard now that I understand a little better how this works + - Would like to make it a grid with 4 rows, and 4 columns + - Rows 0 and 2 will have the scores 'names' + - Rows 1 and 3 will be empty until filled by a score. + - Column 3 will span all rows and show each bonus yatzee as an icon like a star or something. + - Once grided score card is implemented, identify a better mechanism to show that a score has already been counted- instead of the '.' that shows up now. + - Maybe invert the grid color to show which score is being selected by the cursor. +- If upper score >= 63, add a pop-up message at the end game to give a visual indication that a bonus for the top row is being. +- Redo button mapping so that + - the middle button can be used to both hold on dice or confirm score. Leaves the down arrow open for something else. + - Would be nice if up/down could be used to move through the scorecard and left/right move through the dice, but then theres not enough buttons for ROLL so idk yet. +- Learn more about C so that I can move stuff to a header file like everyone else does. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/application.fam new file mode 100644 index 000000000..d46228ec9 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/application.fam @@ -0,0 +1,13 @@ +App( + appid="yatzee", + name="Yatzee", + apptype=FlipperAppType.EXTERNAL, + entry_point="yatzee_main", + requires=["gui"], + stack_size=4 * 1024, + order=99, + fap_icon="images/yatzee_icon_10px.png", + fap_category="Games_Extra", + fap_icon_assets="images", + +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_1.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_1.png new file mode 100644 index 000000000..84f86cbb4 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_2.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_2.png new file mode 100644 index 000000000..bfdc37c36 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_3.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_3.png new file mode 100644 index 000000000..4c0db3f26 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_4.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_4.png new file mode 100644 index 000000000..682f26d3a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_5.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_5.png new file mode 100644 index 000000000..c251d3a78 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_5.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_6.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_6.png new file mode 100644 index 000000000..77e300d88 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/die_6.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/yatzee_icon_10px.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/yatzee_icon_10px.png new file mode 100644 index 000000000..38dfd172f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/images/yatzee_icon_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/yatzee.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/yatzee.c new file mode 100644 index 000000000..f0b44b7e5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero-yatzee-main/yatzee.c @@ -0,0 +1,780 @@ +#include "yatzee_icons.h" + +#include +#include + +#include +#include +#include + +#include +#include + +#define BASE_X 18 +#define BASE_Y 44 +#define DICE_OFFSET 12 +#define HOLD "*" +#define MAX_DICE 5 +#define NUM_SCORES 13 + +bool new_game = true; +bool game_over = false; +bool bonus_added = false; +int8_t num_bonus_yatzees = 0; + +// struct to hold image posistion for dice +typedef struct { + // +-----x + // | + // | + // y + uint8_t x; + uint8_t y; +} ImagePosition; + + +typedef struct { + char* name; + uint32_t value; + bool used; + int8_t row; + int8_t col; + uint8_t (*fn)(); // pointer to function that calculates score +} Score; + +typedef struct { + uint8_t index; + uint8_t value; + bool isHeld; +} Die; + +typedef struct { + int index; + char* symbol; +} Cursor; + +// locations for the dice images +ImagePosition position[5] = { + {.x = BASE_X-DICE_OFFSET, .y = BASE_Y}, + {.x = BASE_X*2-DICE_OFFSET, .y = BASE_Y}, + {.x = BASE_X*3-DICE_OFFSET, .y = BASE_Y}, + {.x = BASE_X*4-DICE_OFFSET, .y = BASE_Y}, + {.x = BASE_X*5-DICE_OFFSET, .y = BASE_Y}, +}; + +// these are the positions that the score cursor will cycle through +ImagePosition score_positions[13] = { + {.x=15, .y=0}, + {.x=15, .y=9}, + {.x=15, .y=18}, + {.x=15, .y=27}, + {.x=44, .y=0}, + {.x=44, .y=9}, + {.x=44, .y=18}, + {.x=44, .y=27}, + {.x=77, .y=0}, + {.x=77, .y=9}, + {.x=77, .y=18}, + {.x=77, .y=27}, + {.x=91, .y=21}, +}; + +// cursor to select dice +Cursor cursor = { + .index = 0, + .symbol = "^" +}; + +// cursor to select score +Cursor scoreCursor = { + .index = -1, + .symbol = "_" +}; + +// setup array to store dice info +Die die[5] = { + {.index = 0, .value = 1, .isHeld = false}, + {.index = 1, .value = 1, .isHeld = false}, + {.index = 2, .value = 1, .isHeld = false}, + {.index = 3, .value = 1, .isHeld = false}, + {.index = 4, .value = 1, .isHeld = false}, +}; + +uint8_t upperScore = 0; +int32_t lowerScore = 0; +int32_t totalScore = 0; +uint8_t roll = 0; +uint8_t totalrolls = 0; + +// ############################################# +// # The following methods add the score for # +// # whichever number is mentioned. # +// ############################################# +static uint8_t ones() { + uint8_t sum = 0; + for (uint8_t i = 0; i < 5; i++) { + if (die[i].value == 1) { + sum++; + } + } + return sum; +} + +static uint8_t twos() { + uint8_t sum = 0; + for (uint8_t i = 0; i < 5; i++) { + if (die[i].value == 2) { + sum = sum+2; + } + } + return sum; +} + +static uint8_t threes() { + uint8_t sum = 0; + for (uint8_t i = 0; i < 5; i++) { + if (die[i].value == 3) { + sum = sum+3; + } + } + return sum; +} + +static uint8_t fours() { + uint8_t sum = 0; + for (uint8_t i = 0; i < 5; i++) { + if (die[i].value == 4) { + sum = sum+4; + } + } + return sum; +} + +static uint8_t fives() { + uint8_t sum = 0; + for (uint8_t i = 0; i < 5; i++) { + if (die[i].value == 5) { + sum = sum+5; + } + } + return sum; +} + +static uint8_t sixes() { + uint8_t sum = 0; + for (uint8_t i = 0; i < 5; i++) { + if (die[i].value == 6) { + sum = sum+6; + } + } + return sum; +} + +// #################################################### +// # Helper methods for the special score types # +// # defined before them so they can be used # +// # since this whole thing is a linear mess # +// # lol. # +// # add_dice: # +// # inputs: none # +// # output: int8_t value of roll # +// # check_if_score_used: +// # inputs: Score +// # output: true if score.used = true +// # # # # # # # # # # # # # # # # # # # # # # # # # # +int8_t add_dice() { + int8_t sum = 0; + for (int8_t i=0; i 2) { + score = add_dice(); + } + } + } + return score; +} + +static uint8_t fourkind() { + int8_t score = 0; + for (int8_t num=1; num<7; num++) { + int8_t sum = 0; + + for (int8_t i=0; i 3) { + score = add_dice(); + } + } + } + return score; +} + +static uint8_t fullhouse() { + + bool check1 = false; + bool check2 = false; + int8_t val1 = 0; + int8_t val2 = 0; + UNUSED(val2); + UNUSED(val1); + + //check 1 for three of a kind + for (int8_t num=1; num<7; num++) { + int8_t sum = 0; + + for (int8_t i=0; i 2) { + val1 = die[i].value; + check1 = true; + } + } + } + + // return if check 1 failed + if (check1 == false) { + return 0; + } + + // check 2 for two of a kind. + for (int8_t num=1; num<7; num++) { + if (num==val1) {continue;} + int8_t sum = 0; + + for (int8_t i=0; i 1) { + val2 = die[i].value; + check2 = true; + } + } + if (check1 && check2) { + return 25; + } + } + return 0; +} + +// # # # # # # # # # # # # # # # # # # # # # # # # # # # +// # I'm dumb so I asked ChatGPT to write the # +// # smallstraight function for me. Then I adapted it # +// # fo the largestraight function. # +// # # # # # # # # # # # # # # # # # # # # # # # # # # # +static uint8_t smallstraight() { + // Create a new array with the frequencies of the different die faces + int8_t frequencies[6] = {0}; + + for (int8_t i = 0; i < 5; i++) { + int8_t face = die[i].value; + frequencies[face - 1]++; + } + + // Check if there is a sequence of 4 consecutive die faces with at least one die + bool found_small_straight = false; + for (int i = 0; i < 3 && !found_small_straight; i++) { + if (frequencies[i] > 0 && frequencies[i + 1] > 0 && frequencies[i + 2] > 0 && frequencies[i + 3] > 0) { + found_small_straight = true; + } + } + + if (found_small_straight) { + return 30; + } else { + return 0; + } +} + +static uint8_t largestraight() { + // Create a new array with the frequencies of the different die faces + int8_t frequencies[6] = {0}; + + for (int8_t i = 0; i < 5; i++) { + int8_t face = die[i].value; + frequencies[face - 1]++; + } + + // Check if there is a sequence of 4 consecutive die faces with at least one die + bool found_large_straight = false; + for (int i = 0; i < 3 && !found_large_straight; i++) { + if (frequencies[i] > 0 && frequencies[i + 1] > 0 && frequencies[i + 2] > 0 && frequencies[i + 3] > 0 && frequencies[i + 4] > 0) { + found_large_straight = true; + } + } + + if (found_large_straight) { + return 40; + } else { + return 0; + } +} + +static uint8_t chance() { + // chance allows your roll to count for the raw number of pips showing + int8_t sum = 0; + for (int8_t i = 0; i63 at the end of the game, + // a 35 point bonus is added to the total score + snprintf(buffer, sizeof(buffer), "Sub\n%u", upperScore); + elements_multiline_text_aligned(canvas, 117, 0, AlignCenter, AlignTop, buffer); + + snprintf(buffer, sizeof(buffer), "Total\n%ld", totalScore); + elements_multiline_text_aligned(canvas, 117, 22, AlignCenter, AlignTop, buffer); + + if (totalrolls == 0) { + snprintf(buffer, sizeof(buffer), "Roll\n%s", " "); + elements_multiline_text_aligned(canvas, 117, 64, AlignCenter, AlignBottom, buffer); + } else { + snprintf(buffer, sizeof(buffer), "Roll\n%u", totalrolls); + elements_multiline_text_aligned(canvas, 117, 64, AlignCenter, AlignBottom, buffer); + } + + // Check for then handle end of game + + // add num_bonus_yatzees to total rounds so that multiple + // yatzees can be scored without impacting the number of rounds before + // the game is over + int8_t total_rounds = num_bonus_yatzees; + // add up number of scores counted so far + for (int8_t i = 0; i= 63 && bonus_added == false) { + totalScore+=35; + bonus_added = true; + } + // set game over to true and tell the user the game is over + game_over = true; + elements_button_center(canvas, "Game Over"); + } + } +} + +// define the callback for helping ViewPort get InputEvent and place it in the event_queue defined in the main method +static void app_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +// roll them diiiiceeee +static void roll_dice() { + // increment roll count + totalrolls++; + for (uint8_t i = 0; i < MAX_DICE; i++) { + // dont reroll if the dice is being held + if (die[i].isHeld == false) { + die[i].value = 1 + rand() % 6; + } + } + // if 3 rolls have been used, force user to select a score. + if(totalrolls == 3) { + scoreCursor.index = 0; + } +} + +static void clear_board() { + // reset board after adding score + totalrolls = 0; + for (int8_t i=0; i < MAX_DICE; i++) { + die[i].isHeld = false; + } + scoreCursor.index = -1; + cursor.index = 0; +} + +static void add_score() { + // return when scoring is not possible + if (cursor.index != -1 || totalrolls == 0 || (scorecard[scoreCursor.index].used && strcmp(scorecard[scoreCursor.index].name,"Yz")!=0)){ + return; + } + + // extra yatzee scores + if (scoreCursor.index == 12 && scorecard[scoreCursor.index].used) { + uint8_t yatzee_score = (*scorecard[12].fn)(); + scorecard[12].value += 2*yatzee_score; + lowerScore+=100; + num_bonus_yatzees++; + } + + + // upper score + for (int8_t i = 0; i < 6; i++) { + if (scoreCursor.index == i && scorecard[scoreCursor.index].used == false) { + scorecard[i].value =(*scorecard[i].fn)(); + upperScore+=scorecard[i].value; + scorecard[i].used = true; + } + } + + // lower score + for (int8_t i = 6; i < 13; i++) { + if (scoreCursor.index == i && scorecard[scoreCursor.index].used == false) { + scorecard[i].value = (*scorecard[i].fn)(); + lowerScore+=scorecard[i].value; + scorecard[i].used = true; + } + } + + // recalculate total score + totalScore = lowerScore + upperScore; + clear_board(); +} + + + + +// Entry Point +int32_t yatzee_main(void* p) { + UNUSED(p); + + // Initialize event queue to handle incoming events like button presses + // Use FuriMessageQueue as type as defined in furi api + // InputEvents are supported by app_input_callback + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + // Initialize viewport + ViewPort* view_port = view_port_alloc(); + + // Set system callbacks + view_port_draw_callback_set(view_port, app_draw_callback, view_port); + view_port_input_callback_set(view_port, app_input_callback, event_queue); + + // Open GUI & register viewport + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + // hold input event + InputEvent event; + + // Create a loop for the app to run in and handle InputEvents + bool isRunning = true; + + while(isRunning) { + + if (totalrolls == 3) { + cursor.index = -1; + } + if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) { + if((event.type == InputTypePress) || event.type == InputTypeRepeat) { + switch(event.key) { + case InputKeyLeft: + if(cursor.index == -1) { + if(scoreCursor.index == 0 && totalrolls == 3){ + scoreCursor.index = NUM_SCORES-1; + } else if (scoreCursor.index==0) { + scoreCursor.index = -1; + cursor.index = 4; + } else { + scoreCursor.index--; + } + } else { + if(cursor.index == 0) { + cursor.index = -1; + scoreCursor.index = NUM_SCORES-1; + } else { + cursor.index--; + } + } + break; + case InputKeyRight: + // cursor.index == -1 means that scoreCursor is active + if(cursor.index == -1) { + if(scoreCursor.index == NUM_SCORES-1 && totalrolls == 3){ + scoreCursor.index = 0; + } else if (scoreCursor.index == NUM_SCORES-1) { + scoreCursor.index = -1; + cursor.index = 0; + } else { + scoreCursor.index++; + } + // if cursor.index is not -1, then dice cursor is active + } else { + if(cursor.index == 4) { + cursor.index = -1; + scoreCursor.index = 0; + } else { + cursor.index++; + } + } + break; + case InputKeyUp: + + if (totalrolls < 3) { + roll_dice(); + } + // if (check_for_bonus_yatzee() && scorecard[13].used) { + // num_bonus_yatzees++; + // totalScore+=100; + // + // clear_board(); + // } + break; + case InputKeyDown: + add_score(); + break; + case InputKeyOk: + if (new_game) { + new_game = false; + break; + } + if (game_over) { + isRunning = false; + } + if (cursor.index == -1 || totalrolls == 0) { + break; + } + if (die[cursor.index].isHeld == false) { + die[cursor.index].isHeld = true; + } else { + die[cursor.index].isHeld = false; + } + break; + default: + isRunning = false; + break; + } + } + } + // after every event, update view_port + // uses app_draw_callback which is set before the game loop begins. + view_port_update(view_port); + } + + // cleanup + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_record_close(RECORD_GUI); + + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/GPIO_reader.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/GPIO_reader.c new file mode 100644 index 000000000..144147b67 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/GPIO_reader.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include "GPIO_reader_item.h" + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + int pin; + int pullMode; +} PluginState; + + +static void render_callback(Canvas* const canvas, void* ctx) { + const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, canvas_width(canvas) / 2, canvas_height(canvas) / 10, AlignCenter, AlignCenter, "GPIO reader"); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, canvas_width(canvas) / 2, canvas_height(canvas) / 10 * 3, AlignCenter, AlignCenter, + gpio_item_get_pin_name(plugin_state->pin)); + + canvas_draw_str_aligned(canvas, canvas_width(canvas) / 2, canvas_height(canvas) / 10 * 5, AlignCenter, AlignCenter, + gpio_item_get_pull_mode(plugin_state->pullMode)); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, canvas_width(canvas) / 2, canvas_height(canvas) / 10 * 8, AlignCenter, AlignCenter, + gpio_item_get_pin_level(plugin_state->pin)); + + release_mutex((ValueMutex*)ctx, plugin_state); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void GPIO_reader_state_init(PluginState* const plugin_state) { + plugin_state->pin = 0; + plugin_state->pullMode = 0; + gpio_item_configure_pin(plugin_state->pin, plugin_state->pullMode); +} + +int32_t GPIO_reader_app(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + PluginState* plugin_state = malloc(sizeof(PluginState)); + GPIO_reader_state_init(plugin_state); + ValueMutex state_mutex; + if (!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + FURI_LOG_E("GPIO_reader", "cannot create mutex\r\n"); + free(plugin_state); + return 255; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress || event.input.type == InputTypeRepeat) { + switch(event.input.key) { + case InputKeyRight: + plugin_state->pin = (plugin_state->pin + 1) % GPIO_ITEM_COUNT; + gpio_item_configure_pin(plugin_state->pin, plugin_state->pullMode); + break; + case InputKeyLeft: + plugin_state->pin = (plugin_state->pin - 1 + GPIO_ITEM_COUNT) % GPIO_ITEM_COUNT; + gpio_item_configure_pin(plugin_state->pin, plugin_state->pullMode); + break; + case InputKeyUp: + plugin_state->pullMode = (plugin_state->pullMode + 1) % GPIO_PULL_COUNT; + gpio_item_configure_pin(plugin_state->pin, plugin_state->pullMode); + break; + case InputKeyDown: + plugin_state->pullMode = (plugin_state->pullMode - 1 + GPIO_PULL_COUNT) % GPIO_PULL_COUNT; + gpio_item_configure_pin(plugin_state->pin, plugin_state->pullMode); + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } + } else { + FURI_LOG_D("GPIO_reader", "FuriMessageQueue: event timeout"); + // event timeout + } + + view_port_update(view_port); + release_mutex(&state_mutex, plugin_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/GPIO_reader_item.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/GPIO_reader_item.c new file mode 100644 index 000000000..15726722c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/GPIO_reader_item.c @@ -0,0 +1,26 @@ +#include "GPIO_reader_item.h" + +const char* gpio_item_get_pin_name(uint8_t index) { + furi_assert(index < GPIO_ITEM_COUNT); + return gpio_item[index].name; +} + +const char* gpio_item_get_pull_mode(uint8_t pull_mode) { + furi_assert(pull_mode < GPIO_PULL_COUNT); + return gpio_pull_mode[pull_mode].name; +} + +const char* gpio_item_get_pin_level(uint8_t index) { + furi_assert(index < GPIO_ITEM_COUNT); + //furi_hal_gpio_write(gpio_item[index].pin, level); + if (furi_hal_gpio_read(gpio_item[index].pin)){ + return "High"; + }else{ + return "Low"; + } +} + +void gpio_item_configure_pin(uint8_t index, uint8_t pull_mode) { + furi_assert(index < GPIO_ITEM_COUNT); + furi_hal_gpio_init(gpio_item[index].pin, GpioModeInput, gpio_pull_mode[pull_mode].pull, GpioSpeedVeryHigh); +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/GPIO_reader_item.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/GPIO_reader_item.h new file mode 100644 index 000000000..da6bcc89f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/GPIO_reader_item.h @@ -0,0 +1,42 @@ +#ifndef GPIO_READER_ITEM +#define GPIO_READER_ITEM + +#include +#include + +#define GPIO_ITEM_COUNT 8 +#define GPIO_PULL_COUNT 3 + +typedef struct { + const char* name; + const GpioPin* pin; +} GpioItem; + +static const GpioItem gpio_item[GPIO_ITEM_COUNT] = { + {"2: PA7", &gpio_ext_pa7}, + {"3: PA6", &gpio_ext_pa6}, + {"4: PA4", &gpio_ext_pa4}, + {"5: PB3", &gpio_ext_pb3}, + {"6: PB2", &gpio_ext_pb2}, + {"7: PC3", &gpio_ext_pc3}, + {"15: PC1", &gpio_ext_pc1}, + {"16: PC0", &gpio_ext_pc0}, +}; + +typedef struct { + const char* name; + const GpioPull pull; +} GpioPullMode; + +static const GpioPullMode gpio_pull_mode[3] = { + {"high impedence", GpioPullNo}, + {"pull up", GpioPullUp}, + {"pull down", GpioPullDown}, +}; + +const char* gpio_item_get_pin_name(uint8_t index); +const char* gpio_item_get_pin_level(uint8_t index); +void gpio_item_configure_pin(uint8_t index, uint8_t pullMode); +const char* gpio_item_get_pull_mode(uint8_t pull_mode); + +#endif \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/README.md new file mode 100644 index 000000000..c911cf313 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/README.md @@ -0,0 +1,3 @@ +# flipperzero_GPIO_read + +Plugin to read the GPIOs on the Flipper Zero. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/application.fam new file mode 100644 index 000000000..7f1aa05a7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/application.fam @@ -0,0 +1,11 @@ +App( + appid="GPIO_reader", + name="[GPIO] Reader", + apptype=FlipperAppType.EXTERNAL, + entry_point="GPIO_reader_app", + requires=["gui"], + stack_size=1 * 1024, + fap_category="GPIO_Extra", + fap_icon="icon.png", + order=1, +) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/icon.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/icon.png new file mode 100644 index 000000000..201d033e7 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_GPIO_read-main/icon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/README.md new file mode 100644 index 000000000..35426a9e1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/README.md @@ -0,0 +1,16 @@ +# flipperzero-gpioreader + +![image info](./gpioreader.png) + +This is a fork of the `gpio` app built into the flipper, with added functionality to read GPIO inputs. + +Supports pulling high or low. + +Does not (yet) support analog reads. + +Installation instructions (Linux): + + - Clone the following repo: https://github.com/flipperdevices/flipperzero-firmware + - Clone this repo into flipperzero-firmware/applications_user + - Plug in your FlipperZero + - Run `./fbt launch_app APPSRC=flipperzero-gpioreader` from within the flipperzero-firmware folder diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/application.fam new file mode 100644 index 000000000..9a1a63688 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/application.fam @@ -0,0 +1,13 @@ +App( + appid="gpioreader2", + name="[GPIO] Input Reader 2", + apptype=FlipperAppType.EXTERNAL, + entry_point="gpio_app", + cdefines=["APP_GPIOREADER"], + requires=["gui"], + stack_size=1 * 1024, + order=50, + fap_libs=["assets"], + fap_category="GPIO_Extra", + fap_icon="icon.png", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_app.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_app.c new file mode 100644 index 000000000..07a79cb89 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_app.c @@ -0,0 +1,111 @@ +#include "gpio_app_i.h" + +#include +#include + +static bool gpio_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + GpioApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool gpio_app_back_event_callback(void* context) { + furi_assert(context); + GpioApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void gpio_app_tick_event_callback(void* context) { + furi_assert(context); + GpioApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +GpioApp* gpio_app_alloc() { + GpioApp* app = malloc(sizeof(GpioApp)); + + app->gui = furi_record_open(RECORD_GUI); + + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, gpio_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, gpio_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, gpio_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + GpioAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + app->gpio_test = gpio_test_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test)); + app->gpio_reader = gpio_reader_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, GpioAppViewGpioReader, gpio_reader_get_view(app->gpio_reader)); + + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, GpioAppViewUsbUartCloseRpc, widget_get_view(app->widget)); + + app->gpio_usb_uart = gpio_usb_uart_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, GpioAppViewUsbUart, gpio_usb_uart_get_view(app->gpio_usb_uart)); + + view_dispatcher_add_view( + app->view_dispatcher, + GpioAppViewUsbUartCfg, + variable_item_list_get_view(app->var_item_list)); + + scene_manager_next_scene(app->scene_manager, GpioSceneStart); + + return app; +} + +void gpio_app_free(GpioApp* app) { + furi_assert(app); + + // Views + view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewVarItemList); + view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest); + view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioReader); + view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart); + view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg); + view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); + variable_item_list_free(app->var_item_list); + widget_free(app->widget); + gpio_test_free(app->gpio_test); + gpio_reader_free(app->gpio_reader); + gpio_usb_uart_free(app->gpio_usb_uart); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Close records + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + + free(app); +} + +int32_t gpio_app(void* p) { + UNUSED(p); + GpioApp* gpio_app = gpio_app_alloc(); + + view_dispatcher_run(gpio_app->view_dispatcher); + + gpio_app_free(gpio_app); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_app.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_app.h new file mode 100644 index 000000000..156ddc922 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_app.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct GpioApp GpioApp; + +#ifdef __cplusplus +} +#endif diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_app_i.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_app_i.h new file mode 100644 index 000000000..52f76949d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_app_i.h @@ -0,0 +1,44 @@ +#pragma once + +#include "gpio_app.h" +#include "gpio_item.h" +#include "scenes/gpio_scene.h" +#include "gpio_custom_event.h" +#include "usb_uart_bridge.h" + +#include +#include +#include +#include +#include +#include +#include +#include "views/gpio_test.h" +#include "views/gpio_reader.h" +#include "views/gpio_usb_uart.h" +#include + +struct GpioApp { + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + Widget* widget; + + VariableItemList* var_item_list; + VariableItem* var_item_flow; + GpioTest* gpio_test; + GpioReader* gpio_reader; + GpioUsbUart* gpio_usb_uart; + UsbUartBridge* usb_uart_bridge; + UsbUartConfig* usb_uart_cfg; +}; + +typedef enum { + GpioAppViewVarItemList, + GpioAppViewGpioTest, + GpioAppViewGpioReader, + GpioAppViewUsbUart, + GpioAppViewUsbUartCfg, + GpioAppViewUsbUartCloseRpc, +} GpioAppView; diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_custom_event.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_custom_event.h new file mode 100644 index 000000000..f5648e10c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_custom_event.h @@ -0,0 +1,14 @@ +#pragma once + +typedef enum { + GpioStartEventOtgOff = 0, + GpioStartEventOtgOn, + GpioStartEventManualControl, + GpioStartEventReader, + GpioStartEventUsbUart, + + GpioCustomEventErrorBack, + + GpioUsbUartEventConfig, + GpioUsbUartEventConfigSet, +} GpioCustomEvent; diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_item.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_item.c new file mode 100644 index 000000000..f516bd258 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_item.c @@ -0,0 +1,60 @@ +#include "gpio_item.h" + +#include + +typedef struct { + const char* name; + const GpioPin* pin; +} GpioItem; + +static const GpioItem gpio_item[GPIO_ITEM_COUNT] = { + {"1.2: PA7", &gpio_ext_pa7}, + {"1.3: PA6", &gpio_ext_pa6}, + {"1.4: PA4", &gpio_ext_pa4}, + {"1.5: PB3", &gpio_ext_pb3}, + {"1.6: PB2", &gpio_ext_pb2}, + {"1.7: PC3", &gpio_ext_pc3}, + {"2.7: PC1", &gpio_ext_pc1}, + {"2.8: PC0", &gpio_ext_pc0}, +}; + +void gpio_item_configure_pin(uint8_t index, GpioMode mode, GpioPull pull) { + furi_assert(index < GPIO_ITEM_COUNT); + furi_hal_gpio_write(gpio_item[index].pin, false); + furi_hal_gpio_init(gpio_item[index].pin, mode, pull, GpioSpeedVeryHigh); +} + +void gpio_item_configure_all_pins(GpioMode mode) { + GpioPull pull = GpioPullNo; + if(mode == GpioModeInput){ + pull = GpioPullDown; + } + for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { + gpio_item_configure_pin(i, mode, pull); + } +} + +void gpio_item_set_pin(uint8_t index, bool level) { + furi_assert(index < GPIO_ITEM_COUNT); + furi_hal_gpio_write(gpio_item[index].pin, level); +} + +bool gpio_item_get_pin(uint8_t index) { + furi_assert(index < GPIO_ITEM_COUNT); + return furi_hal_gpio_read(gpio_item[index].pin); +} + +void gpio_item_set_all_pins(bool level) { + for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { + gpio_item_set_pin(i, level); + } +} + +const char* gpio_item_get_pin_name(uint8_t index) { + furi_assert(index < GPIO_ITEM_COUNT + 1); + if(index == GPIO_ITEM_COUNT) { + return "ALL"; + } else { + return gpio_item[index].name; + } +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_item.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_item.h new file mode 100644 index 000000000..fe73e3851 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpio_item.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#define GPIO_ITEM_COUNT 8 + +void gpio_item_configure_pin(uint8_t index, GpioMode mode, GpioPull pull); + +void gpio_item_configure_all_pins(GpioMode mode); + +void gpio_item_set_pin(uint8_t index, bool level); + +void gpio_item_set_all_pins(bool level); + +const char* gpio_item_get_pin_name(uint8_t index); + +bool gpio_item_get_pin(uint8_t index); diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpioreader.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpioreader.png new file mode 100644 index 000000000..218764e78 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/gpioreader.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/icon.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/icon.png new file mode 100644 index 000000000..201d033e7 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/icon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene.c new file mode 100644 index 000000000..d5aa4cbe8 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene.c @@ -0,0 +1,30 @@ +#include "gpio_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const gpio_scene_on_enter_handlers[])(void*) = { +#include "gpio_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const gpio_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "gpio_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const gpio_scene_on_exit_handlers[])(void* context) = { +#include "gpio_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers gpio_scene_handlers = { + .on_enter_handlers = gpio_scene_on_enter_handlers, + .on_event_handlers = gpio_scene_on_event_handlers, + .on_exit_handlers = gpio_scene_on_exit_handlers, + .scene_num = GpioSceneNum, +}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene.h new file mode 100644 index 000000000..15556c8d5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) GpioScene##id, +typedef enum { +#include "gpio_scene_config.h" + GpioSceneNum, +} GpioScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers gpio_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "gpio_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "gpio_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "gpio_scene_config.h" +#undef ADD_SCENE diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_config.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_config.h new file mode 100644 index 000000000..269c32aaf --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_config.h @@ -0,0 +1,6 @@ +ADD_SCENE(gpio, start, Start) +ADD_SCENE(gpio, test, Test) +ADD_SCENE(gpio, reader, Reader) +ADD_SCENE(gpio, usb_uart, UsbUart) +ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg) +ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_reader.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_reader.c new file mode 100644 index 000000000..5995ff253 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_reader.c @@ -0,0 +1,30 @@ +#include "../gpio_app_i.h" + +void gpio_scene_reader_ok_callback(InputType type, void* context) { + furi_assert(context); + GpioApp* app = context; + + if(type == InputTypePress) { + notification_message(app->notifications, &sequence_set_green_255); + } else if(type == InputTypeRelease) { + notification_message(app->notifications, &sequence_reset_green); + } +} + +void gpio_scene_reader_on_enter(void* context) { + GpioApp* app = context; + gpio_item_configure_all_pins(GpioModeInput); + gpio_reader_set_ok_callback(app->gpio_reader, gpio_scene_reader_ok_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioReader); +} + +bool gpio_scene_reader_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void gpio_scene_reader_on_exit(void* context) { + UNUSED(context); + gpio_item_configure_all_pins(GpioModeAnalog); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_start.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_start.c new file mode 100644 index 000000000..71ddd6593 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_start.c @@ -0,0 +1,114 @@ +#include "../gpio_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" +#include + +enum GpioItem { + GpioItemUsbUart, + GpioItemTest, + GpioItemReader, + GpioItemOtg, +}; + +enum GpioOtg { + GpioOtgOff, + GpioOtgOn, + GpioOtgSettingsNum, +}; + +const char* const gpio_otg_text[GpioOtgSettingsNum] = { + "OFF", + "ON", +}; + +static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t index) { + furi_assert(context); + GpioApp* app = context; + if(index == GpioItemTest) { + view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventManualControl); + } else if(index == GpioItemUsbUart) { + view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventUsbUart); + } else if(index == GpioItemReader) { + view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventReader); + } +} + +static void gpio_scene_start_var_list_change_callback(VariableItem* item) { + GpioApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, gpio_otg_text[index]); + if(index == GpioOtgOff) { + view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventOtgOff); + } else if(index == GpioOtgOn) { + view_dispatcher_send_custom_event(app->view_dispatcher, GpioStartEventOtgOn); + } +} + +void gpio_scene_start_on_enter(void* context) { + GpioApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + + VariableItem* item; + variable_item_list_set_enter_callback( + var_item_list, gpio_scene_start_var_list_enter_callback, app); + + variable_item_list_add(var_item_list, "USB-UART Bridge", 0, NULL, NULL); + + variable_item_list_add(var_item_list, "GPIO Manual Control", 0, NULL, NULL); + + variable_item_list_add(var_item_list, "GPIO Manual Read", 0, NULL, NULL); + + item = variable_item_list_add( + var_item_list, + "5V on GPIO", + GpioOtgSettingsNum, + gpio_scene_start_var_list_change_callback, + app); + if(furi_hal_power_is_otg_enabled()) { + variable_item_set_current_value_index(item, GpioOtgOn); + variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOn]); + } else { + variable_item_set_current_value_index(item, GpioOtgOff); + variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOff]); + } + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewVarItemList); +} + +bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { + GpioApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GpioStartEventOtgOn) { + furi_hal_power_enable_otg(); + } else if(event.event == GpioStartEventOtgOff) { + furi_hal_power_disable_otg(); + } else if(event.event == GpioStartEventManualControl) { + scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemTest); + scene_manager_next_scene(app->scene_manager, GpioSceneTest); + } else if(event.event == GpioStartEventReader) { + scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemReader); + scene_manager_next_scene(app->scene_manager, GpioSceneReader); + } else if(event.event == GpioStartEventUsbUart) { + scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); + if(!furi_hal_usb_is_locked()) { + DOLPHIN_DEED(DolphinDeedGpioUartBridge); + scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); + } else { + scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); + } + } + consumed = true; + } + return consumed; +} + +void gpio_scene_start_on_exit(void* context) { + GpioApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_test.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_test.c new file mode 100644 index 000000000..b015d8090 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_test.c @@ -0,0 +1,30 @@ +#include "../gpio_app_i.h" + +void gpio_scene_test_ok_callback(InputType type, void* context) { + furi_assert(context); + GpioApp* app = context; + + if(type == InputTypePress) { + notification_message(app->notifications, &sequence_set_green_255); + } else if(type == InputTypeRelease) { + notification_message(app->notifications, &sequence_reset_green); + } +} + +void gpio_scene_test_on_enter(void* context) { + GpioApp* app = context; + gpio_item_configure_all_pins(GpioModeOutputPushPull); + gpio_test_set_ok_callback(app->gpio_test, gpio_scene_test_ok_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioTest); +} + +bool gpio_scene_test_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void gpio_scene_test_on_exit(void* context) { + UNUSED(context); + gpio_item_configure_all_pins(GpioModeAnalog); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_usb_uart.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_usb_uart.c new file mode 100644 index 000000000..aa41aaf98 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_usb_uart.c @@ -0,0 +1,67 @@ +#include "../gpio_app_i.h" +#include "../usb_uart_bridge.h" + +typedef struct { + UsbUartConfig cfg; + UsbUartState state; +} SceneUsbUartBridge; + +static SceneUsbUartBridge* scene_usb_uart; + +void gpio_scene_usb_uart_callback(GpioCustomEvent event, void* context) { + furi_assert(context); + GpioApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void gpio_scene_usb_uart_on_enter(void* context) { + GpioApp* app = context; + uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart); + if(prev_state == 0) { + scene_usb_uart = malloc(sizeof(SceneUsbUartBridge)); + scene_usb_uart->cfg.vcp_ch = 0; // TODO: settings load + scene_usb_uart->cfg.uart_ch = 0; + scene_usb_uart->cfg.flow_pins = 0; + scene_usb_uart->cfg.baudrate_mode = 0; + scene_usb_uart->cfg.baudrate = 0; + app->usb_uart_bridge = usb_uart_enable(&scene_usb_uart->cfg); + } + + usb_uart_get_config(app->usb_uart_bridge, &scene_usb_uart->cfg); + usb_uart_get_state(app->usb_uart_bridge, &scene_usb_uart->state); + + gpio_usb_uart_set_callback(app->gpio_usb_uart, gpio_scene_usb_uart_callback, app); + scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 0); + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart); + notification_message(app->notifications, &sequence_display_backlight_enforce_on); +} + +bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { + GpioApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 1); + scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg); + return true; + } else if(event.type == SceneManagerEventTypeTick) { + uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt; + uint32_t rx_cnt_last = scene_usb_uart->state.rx_cnt; + usb_uart_get_state(app->usb_uart_bridge, &scene_usb_uart->state); + gpio_usb_uart_update_state( + app->gpio_usb_uart, &scene_usb_uart->cfg, &scene_usb_uart->state); + if(tx_cnt_last != scene_usb_uart->state.tx_cnt) + notification_message(app->notifications, &sequence_blink_blue_10); + if(rx_cnt_last != scene_usb_uart->state.rx_cnt) + notification_message(app->notifications, &sequence_blink_green_10); + } + return false; +} + +void gpio_scene_usb_uart_on_exit(void* context) { + GpioApp* app = context; + uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioSceneUsbUart); + if(prev_state == 0) { + usb_uart_disable(app->usb_uart_bridge); + free(scene_usb_uart); + } + notification_message(app->notifications, &sequence_display_backlight_enforce_auto); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_usb_uart_close_rpc.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_usb_uart_close_rpc.c new file mode 100644 index 000000000..2cb53cab2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_usb_uart_close_rpc.c @@ -0,0 +1,41 @@ +#include "../gpio_app_i.h" +#include "../gpio_custom_event.h" + +void gpio_scene_usb_uart_close_rpc_on_enter(void* context) { + GpioApp* app = context; + + widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); + widget_add_string_multiline_element( + app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); + widget_add_string_multiline_element( + app->widget, + 3, + 30, + AlignLeft, + AlignTop, + FontSecondary, + "Disconnect from\nPC or phone to\nuse this function."); + + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); +} + +bool gpio_scene_usb_uart_close_rpc_on_event(void* context, SceneManagerEvent event) { + GpioApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GpioCustomEventErrorBack) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + } + } + return consumed; +} + +void gpio_scene_usb_uart_close_rpc_on_exit(void* context) { + GpioApp* app = context; + widget_reset(app->widget); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_usb_uart_config.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_usb_uart_config.c new file mode 100644 index 000000000..55b04ed67 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/scenes/gpio_scene_usb_uart_config.c @@ -0,0 +1,169 @@ +#include "../usb_uart_bridge.h" +#include "../gpio_app_i.h" +#include "furi_hal.h" + +typedef enum { + UsbUartLineIndexVcp, + UsbUartLineIndexBaudrate, + UsbUartLineIndexUart, + UsbUartLineIndexFlow, +} LineIndex; + +static const char* vcp_ch[] = {"0 (CLI)", "1"}; +static const char* uart_ch[] = {"13,14", "15,16"}; +static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"}; +static const char* baudrate_mode[] = {"Host"}; +static const uint32_t baudrate_list[] = { + 2400, + 9600, + 19200, + 38400, + 57600, + 115200, + 230400, + 460800, + 921600, +}; + +bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) { + GpioApp* app = context; + furi_assert(app); + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GpioUsbUartEventConfigSet) { + usb_uart_set_config(app->usb_uart_bridge, app->usb_uart_cfg); + return true; + } + } + return false; +} + +void line_ensure_flow_invariant(GpioApp* app) { + // GPIO pins PC0, PC1 (16,15) are unavailable for RTS/DTR when LPUART is + // selected. This function enforces that invariant by resetting flow_pins + // to None if it is configured to 16,15 when LPUART is selected. + + uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4; + VariableItem* item = app->var_item_flow; + variable_item_set_values_count(item, available_flow_pins); + + if(app->usb_uart_cfg->flow_pins >= available_flow_pins) { + app->usb_uart_cfg->flow_pins = 0; + + variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins); + variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]); + } +} + +static void line_vcp_cb(VariableItem* item) { + GpioApp* app = variable_item_get_context(item); + furi_assert(app); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, vcp_ch[index]); + + app->usb_uart_cfg->vcp_ch = index; + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); +} + +static void line_port_cb(VariableItem* item) { + GpioApp* app = variable_item_get_context(item); + furi_assert(app); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, uart_ch[index]); + + if(index == 0) + app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1; + else if(index == 1) + app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1; + + line_ensure_flow_invariant(app); + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); +} + +static void line_flow_cb(VariableItem* item) { + GpioApp* app = variable_item_get_context(item); + furi_assert(app); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, flow_pins[index]); + + app->usb_uart_cfg->flow_pins = index; + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); +} + +static void line_baudrate_cb(VariableItem* item) { + GpioApp* app = variable_item_get_context(item); + furi_assert(app); + uint8_t index = variable_item_get_current_value_index(item); + + char br_text[8]; + + if(index > 0) { + snprintf(br_text, 7, "%lu", baudrate_list[index - 1]); + variable_item_set_current_value_text(item, br_text); + app->usb_uart_cfg->baudrate = baudrate_list[index - 1]; + } else { + variable_item_set_current_value_text(item, baudrate_mode[index]); + app->usb_uart_cfg->baudrate = 0; + } + app->usb_uart_cfg->baudrate_mode = index; + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); +} + +void gpio_scene_usb_uart_cfg_on_enter(void* context) { + GpioApp* app = context; + furi_assert(app); + VariableItemList* var_item_list = app->var_item_list; + + app->usb_uart_cfg = malloc(sizeof(UsbUartConfig)); + usb_uart_get_config(app->usb_uart_bridge, app->usb_uart_cfg); + + VariableItem* item; + char br_text[8]; + + item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app); + variable_item_set_current_value_index(item, app->usb_uart_cfg->vcp_ch); + variable_item_set_current_value_text(item, vcp_ch[app->usb_uart_cfg->vcp_ch]); + + item = variable_item_list_add( + var_item_list, + "Baudrate", + sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1, + line_baudrate_cb, + app); + variable_item_set_current_value_index(item, app->usb_uart_cfg->baudrate_mode); + if(app->usb_uart_cfg->baudrate_mode > 0) { + snprintf(br_text, 7, "%lu", baudrate_list[app->usb_uart_cfg->baudrate_mode - 1]); + variable_item_set_current_value_text(item, br_text); + } else { + variable_item_set_current_value_text( + item, baudrate_mode[app->usb_uart_cfg->baudrate_mode]); + } + + item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app); + variable_item_set_current_value_index(item, app->usb_uart_cfg->uart_ch); + variable_item_set_current_value_text(item, uart_ch[app->usb_uart_cfg->uart_ch]); + + item = variable_item_list_add( + var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app); + variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins); + variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]); + app->var_item_flow = item; + line_ensure_flow_invariant(app); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg)); + + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCfg); +} + +void gpio_scene_usb_uart_cfg_on_exit(void* context) { + GpioApp* app = context; + scene_manager_set_scene_state( + app->scene_manager, + GpioAppViewUsbUartCfg, + variable_item_list_get_selected_item_index(app->var_item_list)); + variable_item_list_reset(app->var_item_list); + free(app->usb_uart_cfg); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/usb_uart_bridge.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/usb_uart_bridge.c new file mode 100644 index 000000000..1a82dbdc2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/usb_uart_bridge.c @@ -0,0 +1,375 @@ +#include "usb_uart_bridge.h" +#include "furi_hal.h" +#include +#include "usb_cdc.h" +#include "cli/cli_vcp.h" +#include +#include "cli/cli.h" + +#define USB_CDC_PKT_LEN CDC_DATA_SZ +#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) + +#define USB_CDC_BIT_DTR (1 << 0) +#define USB_CDC_BIT_RTS (1 << 1) + +static const GpioPin* flow_pins[][2] = { + {&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3 + {&gpio_ext_pb2, &gpio_ext_pc3}, // 6, 7 + {&gpio_ext_pc0, &gpio_ext_pc1}, // 16, 15 +}; + +typedef enum { + WorkerEvtStop = (1 << 0), + WorkerEvtRxDone = (1 << 1), + + WorkerEvtTxStop = (1 << 2), + WorkerEvtCdcRx = (1 << 3), + + WorkerEvtCfgChange = (1 << 4), + + WorkerEvtLineCfgSet = (1 << 5), + WorkerEvtCtrlLineSet = (1 << 6), + +} WorkerEvtFlags; + +#define WORKER_ALL_RX_EVENTS \ + (WorkerEvtStop | WorkerEvtRxDone | WorkerEvtCfgChange | WorkerEvtLineCfgSet | \ + WorkerEvtCtrlLineSet) +#define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtCdcRx) + +struct UsbUartBridge { + UsbUartConfig cfg; + UsbUartConfig cfg_new; + + FuriThread* thread; + FuriThread* tx_thread; + + FuriStreamBuffer* rx_stream; + + FuriMutex* usb_mutex; + + FuriSemaphore* tx_sem; + + UsbUartState st; + + FuriApiLock cfg_lock; + + uint8_t rx_buf[USB_CDC_PKT_LEN]; +}; + +static void vcp_on_cdc_tx_complete(void* context); +static void vcp_on_cdc_rx(void* context); +static void vcp_state_callback(void* context, uint8_t state); +static void vcp_on_cdc_control_line(void* context, uint8_t state); +static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config); + +static const CdcCallbacks cdc_cb = { + vcp_on_cdc_tx_complete, + vcp_on_cdc_rx, + vcp_state_callback, + vcp_on_cdc_control_line, + vcp_on_line_config, +}; + +/* USB UART worker */ + +static int32_t usb_uart_tx_thread(void* context); + +static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { + UsbUartBridge* usb_uart = (UsbUartBridge*)context; + + if(ev == UartIrqEventRXNE) { + furi_stream_buffer_send(usb_uart->rx_stream, &data, 1, 0); + furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtRxDone); + } +} + +static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { + furi_hal_usb_unlock(); + if(vcp_ch == 0) { + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_close(cli); + furi_record_close(RECORD_CLI); + furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); + } else { + furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_open(cli, &cli_vcp); + furi_record_close(RECORD_CLI); + } + furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart); +} + +static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { + UNUSED(usb_uart); + furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL); + if(vcp_ch != 0) { + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_close(cli); + furi_record_close(RECORD_CLI); + } +} + +static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) { + if(uart_ch == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(uart_ch == FuriHalUartIdLPUART1) { + furi_hal_uart_init(uart_ch, 115200); + } + furi_hal_uart_set_irq_cb(uart_ch, usb_uart_on_irq_cb, usb_uart); +} + +static void usb_uart_serial_deinit(UsbUartBridge* usb_uart, uint8_t uart_ch) { + UNUSED(usb_uart); + furi_hal_uart_set_irq_cb(uart_ch, NULL, NULL); + if(uart_ch == FuriHalUartIdUSART1) + furi_hal_console_enable(); + else if(uart_ch == FuriHalUartIdLPUART1) + furi_hal_uart_deinit(uart_ch); +} + +static void usb_uart_set_baudrate(UsbUartBridge* usb_uart, uint32_t baudrate) { + if(baudrate != 0) { + furi_hal_uart_set_br(usb_uart->cfg.uart_ch, baudrate); + usb_uart->st.baudrate_cur = baudrate; + } else { + struct usb_cdc_line_coding* line_cfg = + furi_hal_cdc_get_port_settings(usb_uart->cfg.vcp_ch); + if(line_cfg->dwDTERate > 0) { + furi_hal_uart_set_br(usb_uart->cfg.uart_ch, line_cfg->dwDTERate); + usb_uart->st.baudrate_cur = line_cfg->dwDTERate; + } + } +} + +static void usb_uart_update_ctrl_lines(UsbUartBridge* usb_uart) { + if(usb_uart->cfg.flow_pins != 0) { + furi_assert((size_t)(usb_uart->cfg.flow_pins - 1) < COUNT_OF(flow_pins)); + uint8_t state = furi_hal_cdc_get_ctrl_line_state(usb_uart->cfg.vcp_ch); + + furi_hal_gpio_write(flow_pins[usb_uart->cfg.flow_pins - 1][0], !(state & USB_CDC_BIT_RTS)); + furi_hal_gpio_write(flow_pins[usb_uart->cfg.flow_pins - 1][1], !(state & USB_CDC_BIT_DTR)); + } +} + +static int32_t usb_uart_worker(void* context) { + UsbUartBridge* usb_uart = (UsbUartBridge*)context; + + memcpy(&usb_uart->cfg, &usb_uart->cfg_new, sizeof(UsbUartConfig)); + + usb_uart->rx_stream = furi_stream_buffer_alloc(USB_UART_RX_BUF_SIZE, 1); + + usb_uart->tx_sem = furi_semaphore_alloc(1, 1); + usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + usb_uart->tx_thread = + furi_thread_alloc_ex("UsbUartTxWorker", 512, usb_uart_tx_thread, usb_uart); + + usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch); + usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch); + usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate); + if(usb_uart->cfg.flow_pins != 0) { + furi_assert((size_t)(usb_uart->cfg.flow_pins - 1) < COUNT_OF(flow_pins)); + furi_hal_gpio_init_simple( + flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeOutputPushPull); + furi_hal_gpio_init_simple( + flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeOutputPushPull); + usb_uart_update_ctrl_lines(usb_uart); + } + + furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtCdcRx); + + furi_thread_start(usb_uart->tx_thread); + + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); + furi_check(!(events & FuriFlagError)); + if(events & WorkerEvtStop) break; + if(events & WorkerEvtRxDone) { + size_t len = furi_stream_buffer_receive( + usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0); + if(len > 0) { + if(furi_semaphore_acquire(usb_uart->tx_sem, 100) == FuriStatusOk) { + usb_uart->st.rx_cnt += len; + furi_check( + furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk); + furi_hal_cdc_send(usb_uart->cfg.vcp_ch, usb_uart->rx_buf, len); + furi_check(furi_mutex_release(usb_uart->usb_mutex) == FuriStatusOk); + } else { + furi_stream_buffer_reset(usb_uart->rx_stream); + } + } + } + if(events & WorkerEvtCfgChange) { + if(usb_uart->cfg.vcp_ch != usb_uart->cfg_new.vcp_ch) { + furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop); + furi_thread_join(usb_uart->tx_thread); + + usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); + usb_uart_vcp_init(usb_uart, usb_uart->cfg_new.vcp_ch); + + usb_uart->cfg.vcp_ch = usb_uart->cfg_new.vcp_ch; + furi_thread_start(usb_uart->tx_thread); + events |= WorkerEvtCtrlLineSet; + events |= WorkerEvtLineCfgSet; + } + if(usb_uart->cfg.uart_ch != usb_uart->cfg_new.uart_ch) { + furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop); + furi_thread_join(usb_uart->tx_thread); + + usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); + usb_uart_serial_init(usb_uart, usb_uart->cfg_new.uart_ch); + + usb_uart->cfg.uart_ch = usb_uart->cfg_new.uart_ch; + usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate); + + furi_thread_start(usb_uart->tx_thread); + } + if(usb_uart->cfg.baudrate != usb_uart->cfg_new.baudrate) { + usb_uart_set_baudrate(usb_uart, usb_uart->cfg_new.baudrate); + usb_uart->cfg.baudrate = usb_uart->cfg_new.baudrate; + } + if(usb_uart->cfg.flow_pins != usb_uart->cfg_new.flow_pins) { + if(usb_uart->cfg.flow_pins != 0) { + furi_hal_gpio_init_simple( + flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog); + furi_hal_gpio_init_simple( + flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); + } + if(usb_uart->cfg_new.flow_pins != 0) { + furi_assert((size_t)(usb_uart->cfg_new.flow_pins - 1) < COUNT_OF(flow_pins)); + furi_hal_gpio_init_simple( + flow_pins[usb_uart->cfg_new.flow_pins - 1][0], GpioModeOutputPushPull); + furi_hal_gpio_init_simple( + flow_pins[usb_uart->cfg_new.flow_pins - 1][1], GpioModeOutputPushPull); + } + usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins; + events |= WorkerEvtCtrlLineSet; + } + api_lock_unlock(usb_uart->cfg_lock); + } + if(events & WorkerEvtLineCfgSet) { + if(usb_uart->cfg.baudrate == 0) + usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate); + } + if(events & WorkerEvtCtrlLineSet) { + usb_uart_update_ctrl_lines(usb_uart); + } + } + usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); + usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); + + if(usb_uart->cfg.flow_pins != 0) { + furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog); + furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); + } + + furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtTxStop); + furi_thread_join(usb_uart->tx_thread); + furi_thread_free(usb_uart->tx_thread); + + furi_stream_buffer_free(usb_uart->rx_stream); + furi_mutex_free(usb_uart->usb_mutex); + furi_semaphore_free(usb_uart->tx_sem); + + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_open(cli, &cli_vcp); + furi_record_close(RECORD_CLI); + + return 0; +} + +static int32_t usb_uart_tx_thread(void* context) { + UsbUartBridge* usb_uart = (UsbUartBridge*)context; + + uint8_t data[USB_CDC_PKT_LEN]; + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever); + furi_check(!(events & FuriFlagError)); + if(events & WorkerEvtTxStop) break; + if(events & WorkerEvtCdcRx) { + furi_check(furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk); + size_t len = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, data, USB_CDC_PKT_LEN); + furi_check(furi_mutex_release(usb_uart->usb_mutex) == FuriStatusOk); + + if(len > 0) { + usb_uart->st.tx_cnt += len; + furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len); + } + } + } + return 0; +} + +/* VCP callbacks */ + +static void vcp_on_cdc_tx_complete(void* context) { + UsbUartBridge* usb_uart = (UsbUartBridge*)context; + furi_semaphore_release(usb_uart->tx_sem); +} + +static void vcp_on_cdc_rx(void* context) { + UsbUartBridge* usb_uart = (UsbUartBridge*)context; + furi_thread_flags_set(furi_thread_get_id(usb_uart->tx_thread), WorkerEvtCdcRx); +} + +static void vcp_state_callback(void* context, uint8_t state) { + UNUSED(context); + UNUSED(state); +} + +static void vcp_on_cdc_control_line(void* context, uint8_t state) { + UNUSED(state); + UsbUartBridge* usb_uart = (UsbUartBridge*)context; + furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCtrlLineSet); +} + +static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) { + UNUSED(config); + UsbUartBridge* usb_uart = (UsbUartBridge*)context; + furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtLineCfgSet); +} + +UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) { + UsbUartBridge* usb_uart = malloc(sizeof(UsbUartBridge)); + + memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); + + usb_uart->thread = furi_thread_alloc_ex("UsbUartWorker", 1024, usb_uart_worker, usb_uart); + + furi_thread_start(usb_uart->thread); + return usb_uart; +} + +void usb_uart_disable(UsbUartBridge* usb_uart) { + furi_assert(usb_uart); + furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtStop); + furi_thread_join(usb_uart->thread); + furi_thread_free(usb_uart->thread); + free(usb_uart); +} + +void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) { + furi_assert(usb_uart); + furi_assert(cfg); + usb_uart->cfg_lock = api_lock_alloc_locked(); + memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); + furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange); + api_lock_wait_unlock_and_free(usb_uart->cfg_lock); +} + +void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) { + furi_assert(usb_uart); + furi_assert(cfg); + memcpy(cfg, &(usb_uart->cfg_new), sizeof(UsbUartConfig)); +} + +void usb_uart_get_state(UsbUartBridge* usb_uart, UsbUartState* st) { + furi_assert(usb_uart); + furi_assert(st); + memcpy(st, &(usb_uart->st), sizeof(UsbUartState)); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/usb_uart_bridge.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/usb_uart_bridge.h new file mode 100644 index 000000000..b456c3cc4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/usb_uart_bridge.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +typedef struct UsbUartBridge UsbUartBridge; + +typedef struct { + uint8_t vcp_ch; + uint8_t uart_ch; + uint8_t flow_pins; + uint8_t baudrate_mode; + uint32_t baudrate; +} UsbUartConfig; + +typedef struct { + uint32_t rx_cnt; + uint32_t tx_cnt; + uint32_t baudrate_cur; +} UsbUartState; + +UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg); + +void usb_uart_disable(UsbUartBridge* usb_uart); + +void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg); + +void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg); + +void usb_uart_get_state(UsbUartBridge* usb_uart, UsbUartState* st); diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_reader.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_reader.c new file mode 100644 index 000000000..95a425fda --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_reader.c @@ -0,0 +1,164 @@ +#include "gpio_reader.h" +#include "../gpio_item.h" + +#include +#include + +struct GpioReader { + View* view; + GpioReaderOkCallback callback; + void* context; +}; + +typedef struct { + uint8_t pin_idx; + bool pullUp[GPIO_ITEM_COUNT]; +} GpioReaderModel; + +static bool gpio_reader_process_ok(GpioReader* gpio_reader, InputEvent* event); +static bool gpio_reader_process_left(GpioReader* gpio_reader); +static bool gpio_reader_process_right(GpioReader* gpio_reader); + +static void gpio_reader_draw_callback(Canvas* canvas, void* _model) { + GpioReaderModel* model = _model; + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "GPIO Reader"); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned( + canvas, 64, 16, AlignCenter, AlignTop, "A7 A6 A4 B3 B2 C3 C1 C0"); + elements_multiline_text_aligned( + canvas, 64, 40, AlignCenter, AlignTop, "Pull Up"); + int charOffset = 10; + for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { + bool high = gpio_item_get_pin(i); + if(high) { + elements_multiline_text_aligned( + canvas, charOffset, 25, AlignCenter, AlignTop, "1"); + } else { + elements_multiline_text_aligned( + canvas, charOffset, 25, AlignCenter, AlignTop, "0"); + } + + if(model->pullUp[i]) { + elements_multiline_text_aligned( + canvas, charOffset, 50, AlignCenter, AlignTop, "1"); + } else { + elements_multiline_text_aligned( + canvas, charOffset, 50, AlignCenter, AlignTop, "0"); + } + if(i == model->pin_idx) { + elements_multiline_text_aligned( + canvas, charOffset, 53, AlignCenter, AlignTop, "_"); + } + + + charOffset += 16; + } + //~ free(charOffset); +} + +static bool gpio_reader_input_callback(InputEvent* event, void* context) { + furi_assert(context); + GpioReader* gpio_reader = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyRight) { + consumed = gpio_reader_process_right(gpio_reader); + } else if(event->key == InputKeyLeft) { + consumed = gpio_reader_process_left(gpio_reader); + } + } else if(event->key == InputKeyOk) { + consumed = gpio_reader_process_ok(gpio_reader, event); + } + + return consumed; +} + +static bool gpio_reader_process_left(GpioReader* gpio_reader) { + with_view_model( + gpio_reader->view, + GpioReaderModel * model, + { + if(model->pin_idx) { + model->pin_idx--; + } + }, + true); + return true; +} + +static bool gpio_reader_process_right(GpioReader* gpio_reader) { + with_view_model( + gpio_reader->view, + GpioReaderModel * model, + { + if(model->pin_idx < GPIO_ITEM_COUNT-1) { + model->pin_idx++; + } + }, + true); + return true; +} + +static bool gpio_reader_process_ok(GpioReader* gpio_reader, InputEvent* event) { + bool consumed = false; + + with_view_model( + gpio_reader->view, + GpioReaderModel * model, + { + if(event->type == InputTypePress) { + if(model->pullUp[model->pin_idx]){ + gpio_item_configure_pin(model->pin_idx, GpioModeInput, GpioPullDown); + model->pullUp[model->pin_idx] = 0; + consumed = true; + }else{ + gpio_item_configure_pin(model->pin_idx, GpioModeInput, GpioPullUp); + model->pullUp[model->pin_idx] = 1; + consumed = true; + } + } + gpio_reader->callback(event->type, gpio_reader->context); + }, + true); + + return consumed; +} + +GpioReader* gpio_reader_alloc() { + GpioReader* gpio_reader = malloc(sizeof(GpioReader)); + + gpio_reader->view = view_alloc(); + view_allocate_model(gpio_reader->view, ViewModelTypeLocking, sizeof(GpioReaderModel)); + view_set_context(gpio_reader->view, gpio_reader); + view_set_draw_callback(gpio_reader->view, gpio_reader_draw_callback); + view_set_input_callback(gpio_reader->view, gpio_reader_input_callback); + + return gpio_reader; +} + +void gpio_reader_free(GpioReader* gpio_reader) { + furi_assert(gpio_reader); + view_free(gpio_reader->view); + free(gpio_reader); +} + +View* gpio_reader_get_view(GpioReader* gpio_reader) { + furi_assert(gpio_reader); + return gpio_reader->view; +} + +void gpio_reader_set_ok_callback(GpioReader* gpio_reader, GpioReaderOkCallback callback, void* context) { + furi_assert(gpio_reader); + furi_assert(callback); + with_view_model( + gpio_reader->view, + GpioReaderModel * model, + { + UNUSED(model); + gpio_reader->callback = callback; + gpio_reader->context = context; + }, + false); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_reader.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_reader.h new file mode 100644 index 000000000..d027d0138 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_reader.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct GpioReader GpioReader; +typedef void (*GpioReaderOkCallback)(InputType type, void* context); + +GpioReader* gpio_reader_alloc(); + +void gpio_reader_free(GpioReader* gpio_reader); + +View* gpio_reader_get_view(GpioReader* gpio_reader); + +void gpio_reader_set_ok_callback(GpioReader* gpio_reader, GpioReaderOkCallback callback, void* context); diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_test.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_test.c new file mode 100644 index 000000000..69dc0f67b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_test.c @@ -0,0 +1,139 @@ +#include "gpio_test.h" +#include "../gpio_item.h" + +#include + +struct GpioTest { + View* view; + GpioTestOkCallback callback; + void* context; +}; + +typedef struct { + uint8_t pin_idx; +} GpioTestModel; + +static bool gpio_test_process_left(GpioTest* gpio_test); +static bool gpio_test_process_right(GpioTest* gpio_test); +static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event); + +static void gpio_test_draw_callback(Canvas* canvas, void* _model) { + GpioTestModel* model = _model; + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "GPIO Output Mode Test"); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned( + canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin"); + elements_multiline_text_aligned( + canvas, 64, 32, AlignCenter, AlignTop, gpio_item_get_pin_name(model->pin_idx)); +} + +static bool gpio_test_input_callback(InputEvent* event, void* context) { + furi_assert(context); + GpioTest* gpio_test = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyRight) { + consumed = gpio_test_process_right(gpio_test); + } else if(event->key == InputKeyLeft) { + consumed = gpio_test_process_left(gpio_test); + } + } else if(event->key == InputKeyOk) { + consumed = gpio_test_process_ok(gpio_test, event); + } + + return consumed; +} + +static bool gpio_test_process_left(GpioTest* gpio_test) { + with_view_model( + gpio_test->view, + GpioTestModel * model, + { + if(model->pin_idx) { + model->pin_idx--; + } + }, + true); + return true; +} + +static bool gpio_test_process_right(GpioTest* gpio_test) { + with_view_model( + gpio_test->view, + GpioTestModel * model, + { + if(model->pin_idx < GPIO_ITEM_COUNT) { + model->pin_idx++; + } + }, + true); + return true; +} + +static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) { + bool consumed = false; + + with_view_model( + gpio_test->view, + GpioTestModel * model, + { + if(event->type == InputTypePress) { + if(model->pin_idx < GPIO_ITEM_COUNT) { + gpio_item_set_pin(model->pin_idx, true); + } else { + gpio_item_set_all_pins(true); + } + consumed = true; + } else if(event->type == InputTypeRelease) { + if(model->pin_idx < GPIO_ITEM_COUNT) { + gpio_item_set_pin(model->pin_idx, false); + } else { + gpio_item_set_all_pins(false); + } + consumed = true; + } + gpio_test->callback(event->type, gpio_test->context); + }, + true); + + return consumed; +} + +GpioTest* gpio_test_alloc() { + GpioTest* gpio_test = malloc(sizeof(GpioTest)); + + gpio_test->view = view_alloc(); + view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel)); + view_set_context(gpio_test->view, gpio_test); + view_set_draw_callback(gpio_test->view, gpio_test_draw_callback); + view_set_input_callback(gpio_test->view, gpio_test_input_callback); + + return gpio_test; +} + +void gpio_test_free(GpioTest* gpio_test) { + furi_assert(gpio_test); + view_free(gpio_test->view); + free(gpio_test); +} + +View* gpio_test_get_view(GpioTest* gpio_test) { + furi_assert(gpio_test); + return gpio_test->view; +} + +void gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, void* context) { + furi_assert(gpio_test); + furi_assert(callback); + with_view_model( + gpio_test->view, + GpioTestModel * model, + { + UNUSED(model); + gpio_test->callback = callback; + gpio_test->context = context; + }, + false); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_test.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_test.h new file mode 100644 index 000000000..5cbd11e82 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_test.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct GpioTest GpioTest; +typedef void (*GpioTestOkCallback)(InputType type, void* context); + +GpioTest* gpio_test_alloc(); + +void gpio_test_free(GpioTest* gpio_test); + +View* gpio_test_get_view(GpioTest* gpio_test); + +void gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, void* context); diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_usb_uart.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_usb_uart.c new file mode 100644 index 000000000..c7406d29b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_usb_uart.c @@ -0,0 +1,161 @@ +#include "../usb_uart_bridge.h" +#include "../gpio_app_i.h" +#include "furi_hal.h" +#include + +struct GpioUsbUart { + View* view; + GpioUsbUartCallback callback; + void* context; +}; + +typedef struct { + uint32_t baudrate; + uint32_t tx_cnt; + uint32_t rx_cnt; + uint8_t vcp_port; + uint8_t tx_pin; + uint8_t rx_pin; + bool tx_active; + bool rx_active; +} GpioUsbUartModel; + +static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) { + GpioUsbUartModel* model = _model; + char temp_str[18]; + elements_button_left(canvas, "Config"); + canvas_draw_line(canvas, 2, 10, 125, 10); + canvas_draw_line(canvas, 44, 52, 123, 52); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 9, "USB Serial"); + canvas_draw_str(canvas, 3, 25, "TX:"); + canvas_draw_str(canvas, 3, 42, "RX:"); + + canvas_set_font(canvas, FontSecondary); + snprintf(temp_str, 18, "COM PORT:%u", model->vcp_port); + canvas_draw_str_aligned(canvas, 126, 8, AlignRight, AlignBottom, temp_str); + snprintf(temp_str, 18, "Pin %u", model->tx_pin); + canvas_draw_str(canvas, 22, 25, temp_str); + snprintf(temp_str, 18, "Pin %u", model->rx_pin); + canvas_draw_str(canvas, 22, 42, temp_str); + + if(model->baudrate == 0) + snprintf(temp_str, 18, "Baud: ????"); + else + snprintf(temp_str, 18, "Baud: %lu", model->baudrate); + canvas_draw_str(canvas, 45, 62, temp_str); + + if(model->tx_cnt < 100000000) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "B."); + canvas_set_font(canvas, FontKeyboard); + snprintf(temp_str, 18, "%lu", model->tx_cnt); + canvas_draw_str_aligned(canvas, 116, 24, AlignRight, AlignBottom, temp_str); + } else { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "KiB."); + canvas_set_font(canvas, FontKeyboard); + snprintf(temp_str, 18, "%lu", model->tx_cnt / 1024); + canvas_draw_str_aligned(canvas, 111, 24, AlignRight, AlignBottom, temp_str); + } + + if(model->rx_cnt < 100000000) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "B."); + canvas_set_font(canvas, FontKeyboard); + snprintf(temp_str, 18, "%lu", model->rx_cnt); + canvas_draw_str_aligned(canvas, 116, 41, AlignRight, AlignBottom, temp_str); + } else { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "KiB."); + canvas_set_font(canvas, FontKeyboard); + snprintf(temp_str, 18, "%lu", model->rx_cnt / 1024); + canvas_draw_str_aligned(canvas, 111, 41, AlignRight, AlignBottom, temp_str); + } + + if(model->tx_active) + canvas_draw_icon(canvas, 48, 14, &I_ArrowUpFilled_14x15); + else + canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15); + + if(model->rx_active) + canvas_draw_icon(canvas, 48, 34, &I_ArrowDownFilled_14x15); + else + canvas_draw_icon(canvas, 48, 34, &I_ArrowDownEmpty_14x15); +} + +static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) { + furi_assert(context); + GpioUsbUart* usb_uart = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + consumed = true; + furi_assert(usb_uart->callback); + usb_uart->callback(GpioUsbUartEventConfig, usb_uart->context); + } + } + + return consumed; +} + +GpioUsbUart* gpio_usb_uart_alloc() { + GpioUsbUart* usb_uart = malloc(sizeof(GpioUsbUart)); + + usb_uart->view = view_alloc(); + view_allocate_model(usb_uart->view, ViewModelTypeLocking, sizeof(GpioUsbUartModel)); + view_set_context(usb_uart->view, usb_uart); + view_set_draw_callback(usb_uart->view, gpio_usb_uart_draw_callback); + view_set_input_callback(usb_uart->view, gpio_usb_uart_input_callback); + + return usb_uart; +} + +void gpio_usb_uart_free(GpioUsbUart* usb_uart) { + furi_assert(usb_uart); + view_free(usb_uart->view); + free(usb_uart); +} + +View* gpio_usb_uart_get_view(GpioUsbUart* usb_uart) { + furi_assert(usb_uart); + return usb_uart->view; +} + +void gpio_usb_uart_set_callback(GpioUsbUart* usb_uart, GpioUsbUartCallback callback, void* context) { + furi_assert(usb_uart); + furi_assert(callback); + + with_view_model( + usb_uart->view, + GpioUsbUartModel * model, + { + UNUSED(model); + usb_uart->callback = callback; + usb_uart->context = context; + }, + false); +} + +void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUartState* st) { + furi_assert(instance); + furi_assert(cfg); + furi_assert(st); + + with_view_model( + instance->view, + GpioUsbUartModel * model, + { + model->baudrate = st->baudrate_cur; + model->vcp_port = cfg->vcp_ch; + model->tx_pin = (cfg->uart_ch == 0) ? (13) : (15); + model->rx_pin = (cfg->uart_ch == 0) ? (14) : (16); + model->tx_active = (model->tx_cnt != st->tx_cnt); + model->rx_active = (model->rx_cnt != st->rx_cnt); + model->tx_cnt = st->tx_cnt; + model->rx_cnt = st->rx_cnt; + }, + true); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_usb_uart.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_usb_uart.h new file mode 100644 index 000000000..854b51f8d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_gpioreader/views/gpio_usb_uart.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "../gpio_custom_event.h" +#include "../usb_uart_bridge.h" + +typedef struct GpioUsbUart GpioUsbUart; +typedef void (*GpioUsbUartCallback)(GpioCustomEvent event, void* context); + +GpioUsbUart* gpio_usb_uart_alloc(); + +void gpio_usb_uart_free(GpioUsbUart* usb_uart); + +View* gpio_usb_uart_get_view(GpioUsbUart* usb_uart); + +void gpio_usb_uart_set_callback(GpioUsbUart* usb_uart, GpioUsbUartCallback callback, void* context); + +void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUartState* st); diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/.github/FUNDING.yml b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/.github/FUNDING.yml new file mode 100644 index 000000000..2cabf7401 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/.github/FUNDING.yml @@ -0,0 +1 @@ +ko_fi: caralynx diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/.github/workflows/build-fap.yml b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/.github/workflows/build-fap.yml new file mode 100644 index 000000000..f65b8609a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/.github/workflows/build-fap.yml @@ -0,0 +1,12 @@ +name: Build FAP +on: [push] + +jobs: + build: + if: ${{ false }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Build + uses: oleksiikutuzov/flipperzero-ufbt-action@v1 diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/README.md b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/README.md new file mode 100644 index 000000000..7f680fcc6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/README.md @@ -0,0 +1,170 @@ +VB Lab Migration Assistant for Flipper Zero +=========================================== + +This app is designed to make transferring your characters from VB Lab and VBC +Lab more convenient. + +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/C0C81P4PX) + +Background +---------- +The Vital Bracelet Arena app is going to be the new companion app for the Vital +Bracelet series of fitness bracelet toys, however the app does not support +account linking from VB Lab and VBC Lab, and requires you to transfer characters +one by one from those apps through your Vital Bracelet. Because the Vital +Bracelet can only hold at most two characters at a time and requires inserting +Dim/VBM to complete the transfer, transferring all of your characters may take a +significant amount of time. The VB Lab Migration Assistant is designed to make +this process faster by allowing you to store an unlimited amount of characters +on the Flipper and to bypass the Dim loading process, therefore allowing you to +transfer your characters without having to flip back and forth between the apps +or wasting time loading data that will never be used. + +Usage +----- + +### 1. Register Vital Bracelet + +Registering your Vital Bracelet to the Flipper creates template data used for +emulating the VB using the Flipper. + +- Select "Register Vital Bracelet" from Migration Assistant's main menu. +- If you have an active character on your Vital Bracelet, **transfer it to VB + Lab/Arena**. The active character slot on the VB has to be empty to avoid + issues when transferring character from VB Lab/Arena to the Flipper. Press the + right key on Flipper. + +If using VB Lab: +- If there is an active character in VB Lab, put it in storage. +- On VB Lab's "Scan" page, tap "Vital Bracelet -> App". Press the right key on + Flipper. + +If using VB Arena: +- Select the device type matching your VB and any franchise. +- Tap the "Send" button. Press the right key on Flipper. + +- Tap the Flipper to your Vital Bracelet. You do not have to put the VB in scan + mode. When Flipper finishes reading, it will beep. +- On VB Lab, tap "Send", or on VB Arena, tap "Transfer Data", and tap the + Flipper to your phone. VB Lab/Arena will give you an error message. This is + normal, and you can dismiss it. The Flipper will beep. Wait for it to switch + to the next screen. +- Tap the Flipper to the original Vital Bracelet again. The Flipper will beep + when it's finished reading. +- Name the Vital Bracelet and select "Save". + +The Vital Bracelet info will show after saving. + +### 2. Select Vital Bracelet, Vital Bracelet Info + +Use the "Select Vital Bracelet" menu to load a previously registered VB. After +loading, you will see information about this Vital Bracelet: + +- Registered name +- Vital Bracelet type +- Number of characters captured + +Press the right key to see a menu of options you can use with the registered VB. + +### Spoof Version + +Vital Bracelet Arena splits Vital Bracelet and Vital Hero Digimon into +mutually-exclusive categories, and currently transferring VBM characters from +VBDM has not been tested, so if you want to transfer between categories, you can +select the Vital Bracelet version to spoof to change the VB type and force the +app to perform transfers between categories. + +Note that spoofing a Vital Bracelet Characters from a non-VBC registration and +vice versa will not work because different encryption keys are used. + +### Unlink Account + +This option unsets the account flag in the NFC data so the character is not +bound to any particular account, and you can transfer it to a different Vital +Bracelet that is using a different account when using VB Arena. + +If enabled when transferring from app, the flag will be unset on the capture and +will still be unset when transferring to app. If enabled when transferring to +app, the flag will be unset on all transfers, regardless whether the flag was +set when captured. + +Note that VB Lab may still complain because it also uses character ID embedded +in character data to check whether the character belongs to the current account. + +### 3. Transfer App > Flipper + +Use this mode to transfer characters from VB Lab to Flipper. + +- An instructions screen is displayed. Press right key to continue. + +If using VB Lab: +- On your VB Lab app, wake up the character you want to send (if you have not + already). +- Go to the "Scan" screen, and tap "App -> Vital Bracelet". Select "Check Dim + Card". If prompted to send Special Missions or items, tap "No". + +If using VB Arena: +- Select the device and franchise for the character you want to transfer. +- Tap the "Assign" button. +- Press and hold on the character you want to transfer. +- Tap the "Send" button. +- Tap "OK". +- Tap "Check Card". + +- Tap the Flipper to your phone. Release when VB Lab indicates to do so, and + wait for Flipper to beep. +- Tap "Check Dim/Card Installation" on VB Lab/Arena, and tap Flipper to your + phone. Flipper will beep. +- Tap "Send" on VB Lab, or "Transfer Data" on VB Arena, and tap Flipper to your + phone. Hold it there until VB Lab/Arena shows the transfer animation. Flipper + will beep after it has received the character. +- Tap "OK" on VB Lab/Arena. Flipper will have automatically reloaded the + template so you can repeat the above again to transfer more characters. +- When you are done transferring characters, press the left key on Flipper to + exit the transfer mode. + +Note if you have previously transferred characters to Flipper, the new +characters you transfer will be put at the end of the sequence of characters +when transferring from Flipper to app. + +### 4. Transfer Flipper > App + +Use this mode to transfer characters from Flipper to VB Lab. + +- An instructions screen is displayed. Press right key to continue. + +If using VB Lab: +- If there is an active character in VB Lab, put it in storage. +- On VB Lab's "Scan" page, tap "Vital Bracelet -> App". +- Tap "Send". + +If using VB Arena: +- Select the device type matching your VB and any franchise. +- Tap the "Send" button. +- Tap the "Transfer Data" button. + +- Tap your Flipper to your phone, and hold until VB Lab/Arena shows the +- transfer animation. Flipper will beep when it detected the transfer and will +- load the next character. +- Repeat the above until all of your characters have been transferred. +- If you want to skip a certain character, press the right key to skip and load +- the next character. +- You will see the "Transfers complete" screen on Flipper when all characters +- have been transferred. Press the right key to return to the menu. + +### 5. Clear Captures + +If you are doing transfers in batches, you can clear the currently captured +characters so you do not have to skip them when transferring to app. Check the +details, and press the right key to confirm deletion. + +### 6. Delete Vital Bracelet + +If you want to unregister the Vital Bracelet, you can use this option to delete +its captures and remove it from the "Select Vital Bracelet" list. Check the +details, and press the right key to confirm deletion. + +Credits +------- + +Graphics by [Aderek](https://twitter.com/AderekArt) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/application.fam b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/application.fam new file mode 100644 index 000000000..2c5ba480d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/application.fam @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# VB Lab Migration Assistant for Flipper Zero +# Copyright (C) 2022 cyanic +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +App( + appid="vb_migrate", + name="VB Migration Assistant", + apptype=FlipperAppType.EXTERNAL, + entry_point="vb_migrate_app", + requires=["gui", "storage"], + stack_size=2 * 1024, + fap_version=(1,0), + fap_libs=["nfc", "misc", "mbedtls", "ST25RFAL002", "gcc", "toolbox"], + fap_icon="vb_migrate_10px.png", + fap_category="Misc_Extra", + fap_description="Makes transferring characters with VB Lab less cumbersome", + fap_author="cyanic", + fap_weburl="https://github.com/GMMan/flipperzero-vb-migrate", + fap_icon_assets="icons", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/gui/modules/file_select.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/gui/modules/file_select.c new file mode 100644 index 000000000..c7560af75 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/gui/modules/file_select.c @@ -0,0 +1,481 @@ +#include "file_select.h" +#include +#include + +#define FILENAME_COUNT 4 + +struct FileSelect { + // public + View* view; + Storage* fs_api; + const char* path; + const char* extension; + + bool init_completed; + + FileSelectCallback callback; + void* context; + + char* buffer; + uint8_t buffer_size; +}; + +typedef struct { + FuriString* filename[FILENAME_COUNT]; + uint8_t position; + + uint16_t first_file_index; + uint16_t file_count; + +} FileSelectModel; + +bool file_select_fill_strings(FileSelect* file_select); +bool file_select_fill_count(FileSelect* file_select); +static bool file_select_init_inner(FileSelect* file_select); + +static void file_select_draw_callback(Canvas* canvas, void* _model) { + FileSelectModel* model = _model; + + FuriString* string_buff; + const uint8_t item_height = 16; + const uint8_t item_width = 123; + const uint8_t text_max_width = 115; + + canvas_clear(canvas); + canvas_set_font(canvas, FontSecondary); + + if(model->file_count) { + for(uint8_t i = 0; i < MIN(FILENAME_COUNT, model->file_count); i++) { + if(i == model->position) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, (i * item_height) + 1, item_width, item_height - 2); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 0, (i * item_height) + 1); + canvas_draw_dot(canvas, 0, (i * item_height) + item_height - 2); + canvas_draw_dot(canvas, item_width - 1, (i * item_height) + 1); + canvas_draw_dot(canvas, item_width - 1, (i * item_height) + item_height - 2); + } else { + canvas_set_color(canvas, ColorBlack); + } + + string_buff = furi_string_alloc_set(model->filename[i]); + elements_string_fit_width(canvas, string_buff, text_max_width); + canvas_draw_str( + canvas, 6, (i * item_height) + item_height - 4, furi_string_get_cstr(string_buff)); + + furi_string_free(string_buff); + } + } else { + canvas_draw_str(canvas, 6, item_height, "Empty folder"); + } + elements_scrollbar(canvas, model->first_file_index + model->position, model->file_count); +} + +static bool file_select_input_callback(InputEvent* event, void* context) { + FileSelect* file_select = (FileSelect*)context; + bool consumed = false; + + if((event->type == InputTypeShort) | (event->type == InputTypeRepeat)) { + if(!file_select->init_completed) { + if(!file_select_init_inner(file_select)) { + file_select->callback(false, file_select->context); + } + } else if(event->key == InputKeyUp) { + with_view_model( + file_select->view, + FileSelectModel * model, + { + if(model->position == 0) { + if(model->first_file_index == 0) { + // wrap + int16_t max_first_file_index = model->file_count - FILENAME_COUNT; + model->position = MIN(FILENAME_COUNT - 1, model->file_count - 1); + model->first_file_index = + max_first_file_index < 0 ? 0 : max_first_file_index; + } else { + model->first_file_index--; + } + } else if(model->position == 1) { + if(model->first_file_index == 0) { + model->position--; + } else { + model->first_file_index--; + } + } else { + model->position--; + } + }, + true); + consumed = true; + if(!file_select_fill_strings(file_select)) { + file_select->callback(false, file_select->context); + } + } else if(event->key == InputKeyDown) { + with_view_model( + file_select->view, + FileSelectModel * model, + { + uint16_t max_first_file_index = model->file_count > FILENAME_COUNT ? + model->file_count - FILENAME_COUNT : + 0; + + if(model->position >= MIN(FILENAME_COUNT - 1, model->file_count - 1)) { + if(model->first_file_index >= max_first_file_index) { + // wrap + model->position = 0; + model->first_file_index = 0; + } else { + model->first_file_index++; + } + } else if(model->position >= (FILENAME_COUNT - 2)) { + if(model->first_file_index >= max_first_file_index) { + model->position++; + } else { + model->first_file_index++; + } + } else { + model->position++; + } + }, + true); + consumed = true; + if(!file_select_fill_strings(file_select)) { + file_select->callback(false, file_select->context); + } + } else if(event->key == InputKeyOk) { + if(file_select->callback != NULL) { + size_t files = 0; + if(file_select->buffer) { + with_view_model( + file_select->view, + FileSelectModel * model, + { + files = model->file_count; + strlcpy( + file_select->buffer, + furi_string_get_cstr(model->filename[model->position]), + file_select->buffer_size); + }, + false); + }; + if(files > 0) { + file_select->callback(true, file_select->context); + } + } + consumed = true; + } + } + + return consumed; +} + +static bool file_select_init_inner(FileSelect* file_select) { + bool result = false; + if(file_select->path && file_select->extension && file_select->fs_api) { + if(file_select_fill_count(file_select)) { + if(file_select_fill_strings(file_select)) { + file_select->init_completed = true; + result = true; + } + } + } + + return result; +} + +FileSelect* file_select_alloc() { + FileSelect* file_select = malloc(sizeof(FileSelect)); + file_select->view = view_alloc(); + file_select->fs_api = furi_record_open("storage"); + + view_set_context(file_select->view, file_select); + view_allocate_model(file_select->view, ViewModelTypeLockFree, sizeof(FileSelectModel)); + view_set_draw_callback(file_select->view, file_select_draw_callback); + view_set_input_callback(file_select->view, file_select_input_callback); + + with_view_model( + file_select->view, + FileSelectModel * model, + { + for(uint8_t i = 0; i < FILENAME_COUNT; i++) { + model->filename[i] = furi_string_alloc(); + } + + model->first_file_index = 0; + model->file_count = 0; + }, + false); + + return file_select; +} + +void file_select_free(FileSelect* file_select) { + furi_assert(file_select); + with_view_model( + file_select->view, + FileSelectModel * model, + { + for(uint8_t i = 0; i < FILENAME_COUNT; i++) { + furi_string_free(model->filename[i]); + } + }, + false); + view_free(file_select->view); + free(file_select); + furi_record_close("storage"); +} + +View* file_select_get_view(FileSelect* file_select) { + furi_assert(file_select); + return file_select->view; +} + +void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context) { + file_select->context = context; + file_select->callback = callback; +} + +void file_select_set_filter(FileSelect* file_select, const char* path, const char* extension) { + furi_assert(file_select); + file_select->path = path; + file_select->extension = extension; +} + +void file_select_set_result_buffer(FileSelect* file_select, char* buffer, uint8_t buffer_size) { + file_select->buffer = buffer; + file_select->buffer_size = buffer_size; + + if(file_select->buffer) { + strlcpy(file_select->buffer, "", file_select->buffer_size); + } +} + +bool file_select_init(FileSelect* file_select) { + if(!file_select_init_inner(file_select)) { + file_select->callback(false, file_select->context); + return false; + } else { + return true; + } +} + +static bool filter_file(FileSelect* file_select, FileInfo* file_info, char* name) { + bool result = false; + + if((file_info->flags & FSF_DIRECTORY)) { + if(strcmp(file_select->extension, "*") == 0) { + result = true; + } else if(strstr(name, file_select->extension) != NULL) { + result = true; + } + } + + return result; +} + +bool file_select_fill_strings(FileSelect* file_select) { + furi_assert(file_select); + furi_assert(file_select->fs_api); + furi_assert(file_select->path); + furi_assert(file_select->extension); + + FileInfo file_info; + File* directory = storage_file_alloc(file_select->fs_api); + + uint8_t string_counter = 0; + uint16_t file_counter = 0; + const uint8_t name_length = 100; + char* name = malloc(name_length); + uint16_t first_file_index = 0; + + with_view_model( + file_select->view, + FileSelectModel * model, + { first_file_index = model->first_file_index; }, + false); + + if(!storage_dir_open(directory, file_select->path)) { + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return true; + } + + while(1) { + if(!storage_dir_read(directory, &file_info, name, name_length)) { + break; + } + + if(storage_file_get_error(directory) == FSE_OK) { + if(filter_file(file_select, &file_info, name)) { + if(file_counter >= first_file_index) { + with_view_model( + file_select->view, + FileSelectModel * model, + { + furi_string_set(model->filename[string_counter], name); + + if(strcmp(file_select->extension, "*") != 0) { + furi_string_replace_all_str( + model->filename[string_counter], file_select->extension, ""); + } + }, + true); + string_counter++; + + if(string_counter >= FILENAME_COUNT) { + break; + } + } + file_counter++; + } + } else { + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return false; + } + } + + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return true; +} + +bool file_select_fill_count(FileSelect* file_select) { + furi_assert(file_select); + furi_assert(file_select->fs_api); + furi_assert(file_select->path); + furi_assert(file_select->extension); + + FileInfo file_info; + File* directory = storage_file_alloc(file_select->fs_api); + + uint16_t file_counter = 0; + const uint8_t name_length = 100; + char* name = malloc(name_length); + + if(!storage_dir_open(directory, file_select->path)) { + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return true; + } + + while(1) { + if(!storage_dir_read(directory, &file_info, name, name_length)) { + break; + } + + if(storage_file_get_error(directory) == FSE_OK) { + if(filter_file(file_select, &file_info, name)) { + file_counter++; + } + } else { + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return false; + } + } + + with_view_model( + file_select->view, FileSelectModel * model, { model->file_count = file_counter; }, false); + + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return true; +} + +void file_select_set_selected_file_internal(FileSelect* file_select, const char* filename) { + furi_assert(file_select); + furi_assert(filename); + furi_assert(file_select->fs_api); + furi_assert(file_select->path); + furi_assert(file_select->extension); + + if(strlen(filename) == 0) return; + + FileInfo file_info; + File* directory = storage_file_alloc(file_select->fs_api); + + const uint8_t name_length = 100; + char* name = malloc(name_length); + uint16_t file_position = 0; + bool file_found = false; + + FuriString* filename_str = furi_string_alloc_set_str(filename); + if(strcmp(file_select->extension, "*") != 0) { + furi_string_cat_str(filename_str, file_select->extension); + } + + if(!storage_dir_open(directory, file_select->path)) { + furi_string_free(filename_str); + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return; + } + + while(1) { + if(!storage_dir_read(directory, &file_info, name, name_length)) { + break; + } + + if(storage_file_get_error(directory) == FSE_OK) { + if(filter_file(file_select, &file_info, name)) { + if(strcmp(furi_string_get_cstr(filename_str), name) == 0) { + file_found = true; + break; + } + + file_position++; + } + } else { + furi_string_free(filename_str); + storage_dir_close(directory); + storage_file_free(directory); + free(name); + return; + } + } + + if(file_found) { + with_view_model( + file_select->view, + FileSelectModel * model, + { + uint16_t max_first_file_index = + model->file_count > FILENAME_COUNT ? model->file_count - FILENAME_COUNT : 0; + + model->first_file_index = file_position; + + if(model->first_file_index > 0) { + model->first_file_index -= 1; + } + + if(model->first_file_index >= max_first_file_index) { + model->first_file_index = max_first_file_index; + } + + model->position = file_position - model->first_file_index; + }, + true); + } + + furi_string_free(filename_str); + storage_dir_close(directory); + storage_file_free(directory); + free(name); +} + +void file_select_set_selected_file(FileSelect* file_select, const char* filename) { + file_select_set_selected_file_internal(file_select, filename); + + if(!file_select_fill_strings(file_select)) { + file_select->callback(false, file_select->context); + } +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/gui/modules/file_select.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/gui/modules/file_select.h new file mode 100644 index 000000000..ed3d5b60c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/gui/modules/file_select.h @@ -0,0 +1,31 @@ +/** + * @file file_select.h + * GUI: FileSelect view module API + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FileSelect FileSelect; + +typedef void (*FileSelectCallback)(bool result, void* context); + +FileSelect* file_select_alloc(); + +void file_select_free(FileSelect* file_select); +View* file_select_get_view(FileSelect* file_select); + +void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context); +void file_select_set_filter(FileSelect* file_select, const char* path, const char* extension); +void file_select_set_result_buffer(FileSelect* file_select, char* buffer, uint8_t buffer_size); +bool file_select_init(FileSelect* file_select); +void file_select_set_selected_file(FileSelect* file_select, const char* filename); + +#ifdef __cplusplus +} +#endif diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/gui/modules/variable_item_list_ex.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/gui/modules/variable_item_list_ex.c new file mode 100644 index 000000000..69543891a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/gui/modules/variable_item_list_ex.c @@ -0,0 +1,419 @@ +#include "variable_item_list_ex.h" +#include "gui/canvas.h" +#include +#include +#include +#include + +struct VariableItemEx { + const char* label; + uint8_t current_value_index; + FuriString* current_value_text; + uint8_t values_count; + VariableItemExChangeCallback change_callback; + void* context; + int32_t callback_index; +}; + +ARRAY_DEF(VariableItemExArray, VariableItemEx, M_POD_OPLIST); + +struct VariableItemListEx { + View* view; + VariableItemListExEnterCallback callback; + void* context; +}; + +typedef struct { + VariableItemExArray_t items; + uint8_t position; + uint8_t window_position; +} VariableItemListExModel; + +static void variable_item_list_ex_process_up(VariableItemListEx* variable_item_list); +static void variable_item_list_ex_process_down(VariableItemListEx* variable_item_list); +static void variable_item_list_ex_process_left(VariableItemListEx* variable_item_list); +static void variable_item_list_ex_process_right(VariableItemListEx* variable_item_list); +static void variable_item_list_ex_process_ok(VariableItemListEx* variable_item_list); + +static void variable_item_list_ex_draw_callback(Canvas* canvas, void* _model) { + VariableItemListExModel* model = _model; + + const uint8_t item_height = 16; + const uint8_t item_width = 123; + + canvas_clear(canvas); + + uint8_t position = 0; + VariableItemExArray_it_t it; + + canvas_set_font(canvas, FontSecondary); + for(VariableItemExArray_it(it, model->items); !VariableItemExArray_end_p(it); + VariableItemExArray_next(it)) { + uint8_t item_position = position - model->window_position; + uint8_t items_on_screen = 4; + uint8_t y_offset = 0; + + if(item_position < items_on_screen) { + const VariableItemEx* item = VariableItemExArray_cref(it); + uint8_t item_y = y_offset + (item_position * item_height); + uint8_t item_text_y = item_y + item_height - 4; + + if(position == model->position) { + canvas_set_color(canvas, ColorBlack); + elements_slightly_rounded_box(canvas, 0, item_y + 1, item_width, item_height - 2); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_color(canvas, ColorBlack); + } + + canvas_draw_str(canvas, 6, item_text_y, item->label); + + if(item->current_value_index > 0) { + canvas_draw_str(canvas, 73, item_text_y, "<"); + } + + canvas_draw_str_aligned( + canvas, + (115 + 73) / 2 + 1, + item_text_y, + AlignCenter, + AlignBottom, + furi_string_get_cstr(item->current_value_text)); + + if(item->current_value_index < (item->values_count - 1)) { + canvas_draw_str(canvas, 115, item_text_y, ">"); + } + } + + position++; + } + + elements_scrollbar(canvas, model->position, VariableItemExArray_size(model->items)); +} + +void variable_item_list_ex_set_selected_item(VariableItemListEx* variable_item_list, uint8_t index) { + with_view_model( + variable_item_list->view, + VariableItemListExModel * model, + { + uint8_t position = index; + if(position >= VariableItemExArray_size(model->items)) { + position = 0; + } + + model->position = position; + model->window_position = position; + + if(model->window_position > 0) { + model->window_position -= 1; + } + + if(VariableItemExArray_size(model->items) <= 4) { + model->window_position = 0; + } else { + if(model->window_position >= (VariableItemExArray_size(model->items) - 4)) { + model->window_position = (VariableItemExArray_size(model->items) - 4); + } + } + }, + true); +} + +uint8_t variable_item_list_ex_get_selected_item_index(VariableItemListEx* variable_item_list) { + VariableItemListExModel* model = view_get_model(variable_item_list->view); + uint8_t idx = model->position; + view_commit_model(variable_item_list->view, false); + return idx; +} + +static bool variable_item_list_ex_input_callback(InputEvent* event, void* context) { + VariableItemListEx* variable_item_list = context; + furi_assert(variable_item_list); + bool consumed = false; + + if(event->type == InputTypeShort) { + switch(event->key) { + case InputKeyUp: + consumed = true; + variable_item_list_ex_process_up(variable_item_list); + break; + case InputKeyDown: + consumed = true; + variable_item_list_ex_process_down(variable_item_list); + break; + case InputKeyLeft: + consumed = true; + variable_item_list_ex_process_left(variable_item_list); + break; + case InputKeyRight: + consumed = true; + variable_item_list_ex_process_right(variable_item_list); + break; + case InputKeyOk: + variable_item_list_ex_process_ok(variable_item_list); + break; + default: + break; + } + } else if(event->type == InputTypeRepeat) { + switch(event->key) { + case InputKeyUp: + consumed = true; + variable_item_list_ex_process_up(variable_item_list); + break; + case InputKeyDown: + consumed = true; + variable_item_list_ex_process_down(variable_item_list); + break; + case InputKeyLeft: + consumed = true; + variable_item_list_ex_process_left(variable_item_list); + break; + case InputKeyRight: + consumed = true; + variable_item_list_ex_process_right(variable_item_list); + break; + default: + break; + } + } + + return consumed; +} + +void variable_item_list_ex_process_up(VariableItemListEx* variable_item_list) { + with_view_model( + variable_item_list->view, + VariableItemListExModel * model, + { + uint8_t items_on_screen = 4; + if(model->position > 0) { + model->position--; + if(((model->position - model->window_position) < 1) && + model->window_position > 0) { + model->window_position--; + } + } else { + model->position = VariableItemExArray_size(model->items) - 1; + if(model->position > (items_on_screen - 1)) { + model->window_position = model->position - (items_on_screen - 1); + } + } + }, + true); +} + +void variable_item_list_ex_process_down(VariableItemListEx* variable_item_list) { + with_view_model( + variable_item_list->view, + VariableItemListExModel * model, + { + uint8_t items_on_screen = 4; + if(model->position < (VariableItemExArray_size(model->items) - 1)) { + model->position++; + if((model->position - model->window_position) > (items_on_screen - 2) && + model->window_position < + (VariableItemExArray_size(model->items) - items_on_screen)) { + model->window_position++; + } + } else { + model->position = 0; + model->window_position = 0; + } + }, + true); +} + +VariableItemEx* variable_item_list_ex_get_selected_item(VariableItemListExModel* model) { + VariableItemEx* item = NULL; + + VariableItemExArray_it_t it; + uint8_t position = 0; + for(VariableItemExArray_it(it, model->items); !VariableItemExArray_end_p(it); + VariableItemExArray_next(it)) { + if(position == model->position) { + break; + } + position++; + } + + item = VariableItemExArray_ref(it); + + furi_assert(item); + return item; +} + +void variable_item_list_ex_process_left(VariableItemListEx* variable_item_list) { + with_view_model( + variable_item_list->view, + VariableItemListExModel * model, + { + VariableItemEx* item = variable_item_list_ex_get_selected_item(model); + if(item->current_value_index > 0) { + item->current_value_index--; + if(item->change_callback) { + item->change_callback(item); + } + } + }, + true); +} + +void variable_item_list_ex_process_right(VariableItemListEx* variable_item_list) { + with_view_model( + variable_item_list->view, + VariableItemListExModel * model, + { + VariableItemEx* item = variable_item_list_ex_get_selected_item(model); + if(item->current_value_index < (item->values_count - 1)) { + item->current_value_index++; + if(item->change_callback) { + item->change_callback(item); + } + } + }, + true); +} + +void variable_item_list_ex_process_ok(VariableItemListEx* variable_item_list) { + with_view_model( + variable_item_list->view, + VariableItemListExModel * model, + { + if(variable_item_list->callback) { + const VariableItemEx* variable_item = + VariableItemExArray_cget(model->items, model->position); + variable_item_list->callback( + variable_item_list->context, variable_item->callback_index); + } + }, + false); +} + +VariableItemListEx* variable_item_list_ex_alloc() { + VariableItemListEx* variable_item_list = malloc(sizeof(VariableItemListEx)); + variable_item_list->view = view_alloc(); + view_set_context(variable_item_list->view, variable_item_list); + view_allocate_model( + variable_item_list->view, ViewModelTypeLocking, sizeof(VariableItemListExModel)); + view_set_draw_callback(variable_item_list->view, variable_item_list_ex_draw_callback); + view_set_input_callback(variable_item_list->view, variable_item_list_ex_input_callback); + + with_view_model( + variable_item_list->view, + VariableItemListExModel * model, + { + VariableItemExArray_init(model->items); + model->position = 0; + model->window_position = 0; + }, + true); + + return variable_item_list; +} + +void variable_item_list_ex_free(VariableItemListEx* variable_item_list) { + furi_assert(variable_item_list); + + with_view_model( + variable_item_list->view, + VariableItemListExModel * model, + { + VariableItemExArray_it_t it; + for(VariableItemExArray_it(it, model->items); !VariableItemExArray_end_p(it); + VariableItemExArray_next(it)) { + furi_string_free(VariableItemExArray_ref(it)->current_value_text); + } + VariableItemExArray_clear(model->items); + }, + false); + view_free(variable_item_list->view); + free(variable_item_list); +} + +void variable_item_list_ex_reset(VariableItemListEx* variable_item_list) { + furi_assert(variable_item_list); + + with_view_model( + variable_item_list->view, + VariableItemListExModel * model, + { + VariableItemExArray_it_t it; + for(VariableItemExArray_it(it, model->items); !VariableItemExArray_end_p(it); + VariableItemExArray_next(it)) { + furi_string_free(VariableItemExArray_ref(it)->current_value_text); + } + VariableItemExArray_reset(model->items); + }, + false); +} + +View* variable_item_list_ex_get_view(VariableItemListEx* variable_item_list) { + furi_assert(variable_item_list); + return variable_item_list->view; +} + +VariableItemEx* variable_item_list_ex_add( + VariableItemListEx* variable_item_list, + const char* label, + uint8_t values_count, + VariableItemExChangeCallback change_callback, + void* context, + int32_t callback_index) { + VariableItemEx* item = NULL; + furi_assert(label); + furi_assert(variable_item_list); + + with_view_model( + variable_item_list->view, + VariableItemListExModel * model, + { + item = VariableItemExArray_push_new(model->items); + item->label = label; + item->values_count = values_count; + item->change_callback = change_callback; + item->context = context; + item->current_value_index = 0; + item->current_value_text = furi_string_alloc(); + item->callback_index = callback_index; + }, + true); + + return item; +} + +void variable_item_list_ex_set_enter_callback( + VariableItemListEx* variable_item_list, + VariableItemListExEnterCallback callback, + void* context) { + furi_assert(callback); + with_view_model( + variable_item_list->view, + VariableItemListExModel * model, + { + UNUSED(model); + variable_item_list->callback = callback; + variable_item_list->context = context; + }, + false); +} + +void variable_item_ex_set_current_value_index(VariableItemEx* item, uint8_t current_value_index) { + item->current_value_index = current_value_index; +} + +void variable_item_ex_set_values_count(VariableItemEx* item, uint8_t values_count) { + item->values_count = values_count; +} + +void variable_item_ex_set_current_value_text(VariableItemEx* item, const char* current_value_text) { + furi_string_set(item->current_value_text, current_value_text); +} + +uint8_t variable_item_ex_get_current_value_index(VariableItemEx* item) { + return item->current_value_index; +} + +void* variable_item_ex_get_context(VariableItemEx* item) { + return item->context; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/gui/modules/variable_item_list_ex.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/gui/modules/variable_item_list_ex.h new file mode 100644 index 000000000..3af5886b0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/gui/modules/variable_item_list_ex.h @@ -0,0 +1,117 @@ +/** + * @file variable_item_list.h + * GUI: VariableItemList view module API + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct VariableItemListEx VariableItemListEx; +typedef struct VariableItemEx VariableItemEx; +typedef void (*VariableItemExChangeCallback)(VariableItemEx* item); +typedef void (*VariableItemListExEnterCallback)(void* context, uint32_t index); + +/** Allocate and initialize VariableItemList + * + * @return VariableItemList* + */ +VariableItemListEx* variable_item_list_ex_alloc(); + +/** Deinitialize and free VariableItemList + * + * @param variable_item_list VariableItemList instance + */ +void variable_item_list_ex_free(VariableItemListEx* variable_item_list); + +/** Clear all elements from list + * + * @param variable_item_list VariableItemList instance + */ +void variable_item_list_ex_reset(VariableItemListEx* variable_item_list); + +/** Get VariableItemList View instance + * + * @param variable_item_list VariableItemList instance + * + * @return View instance + */ +View* variable_item_list_ex_get_view(VariableItemListEx* variable_item_list); + +/** Add item to VariableItemList + * + * @param variable_item_list VariableItemList instance + * @param label item name + * @param values_count item values count + * @param change_callback called on value change in gui + * @param context item context + * + * @return VariableItemEx* item instance + */ +VariableItemEx* variable_item_list_ex_add( + VariableItemListEx* variable_item_list, + const char* label, + uint8_t values_count, + VariableItemExChangeCallback change_callback, + void* context, + int32_t callback_index); + +/** Set enter callback + * + * @param variable_item_list VariableItemList instance + * @param callback VariableItemListEnterCallback instance + * @param context pointer to context + */ +void variable_item_list_ex_set_enter_callback( + VariableItemListEx* variable_item_list, + VariableItemListExEnterCallback callback, + void* context); + +void variable_item_list_ex_set_selected_item(VariableItemListEx* variable_item_list, uint8_t index); + +uint8_t variable_item_list_ex_get_selected_item_index(VariableItemListEx* variable_item_list); + +/** Set item current selected index + * + * @param item VariableItemEx* instance + * @param current_value_index The current value index + */ +void variable_item_ex_set_current_value_index(VariableItemEx* item, uint8_t current_value_index); + +/** Set number of values for item + * + * @param item VariableItemEx* instance + * @param values_count The new values count + */ +void variable_item_ex_set_values_count(VariableItemEx* item, uint8_t values_count); + +/** Set item current selected text + * + * @param item VariableItemEx* instance + * @param current_value_text The current value text + */ +void variable_item_ex_set_current_value_text(VariableItemEx* item, const char* current_value_text); + +/** Get item current selected index + * + * @param item VariableItemEx* instance + * + * @return uint8_t current selected index + */ +uint8_t variable_item_ex_get_current_value_index(VariableItemEx* item); + +/** Get item context + * + * @param item VariableItemEx* instance + * + * @return void* item context + */ +void* variable_item_ex_get_context(VariableItemEx* item); + +#ifdef __cplusplus +} +#endif diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/AppInteract_32x27.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/AppInteract_32x27.png new file mode 100644 index 000000000..282d884b6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/AppInteract_32x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/Background_128x64.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/Background_128x64.png new file mode 100644 index 000000000..e3733663d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/Background_128x64.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommComplete_32x20.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommComplete_32x20.png new file mode 100644 index 000000000..cb6d93e6c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommComplete_32x20.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommReady_32x27.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommReady_32x27.png new file mode 100644 index 000000000..d1eba5e87 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommReady_32x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommReturn_10x8.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommReturn_10x8.png new file mode 100644 index 000000000..41d3033fa Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommReturn_10x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommVB_32x20.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommVB_32x20.png new file mode 100644 index 000000000..e32cfde62 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommVB_32x20.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommWave_12x8.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommWave_12x8.png new file mode 100644 index 000000000..6a6dd702c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/CommWave_12x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/Comm_32x20.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/Comm_32x20.png new file mode 100644 index 000000000..53001e6bf Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/Comm_32x20.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/Delete_32x20.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/Delete_32x20.png new file mode 100644 index 000000000..4eb73422a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/Delete_32x20.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/LeftButtonBg_54x15.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/LeftButtonBg_54x15.png new file mode 100644 index 000000000..725d02160 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/LeftButtonBg_54x15.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonLeftStand_14x16.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonLeftStand_14x16.png new file mode 100644 index 000000000..ea0bc9a3b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonLeftStand_14x16.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonLeftWait_15x15.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonLeftWait_15x15.png new file mode 100644 index 000000000..4566fe923 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonLeftWait_15x15.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonRightHappy_14x16.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonRightHappy_14x16.png new file mode 100644 index 000000000..aa7620557 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonRightHappy_14x16.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonRightSad_15x15.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonRightSad_15x15.png new file mode 100644 index 000000000..b847a20fb Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonRightSad_15x15.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonRightWaiting_15x16.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonRightWaiting_15x16.png new file mode 100644 index 000000000..9d6e1a44c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/PulsemonRightWaiting_15x16.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/RightButtonBg_54x15.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/RightButtonBg_54x15.png new file mode 100644 index 000000000..c82b3673f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/RightButtonBg_54x15.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextClearCaptures_49x13.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextClearCaptures_49x13.png new file mode 100644 index 000000000..3b9f6de64 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextClearCaptures_49x13.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextCleared_47x6.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextCleared_47x6.png new file mode 100644 index 000000000..04ff71e73 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextCleared_47x6.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextDeleteVb_49x13.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextDeleteVb_49x13.png new file mode 100644 index 000000000..6792bc4bf Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextDeleteVb_49x13.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextDeleted_46x6.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextDeleted_46x6.png new file mode 100644 index 000000000..5cc3f0813 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextDeleted_46x6.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrLoadCapture_65x13.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrLoadCapture_65x13.png new file mode 100644 index 000000000..829a19d3c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrLoadCapture_65x13.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrLoadTemplate_69x13.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrLoadTemplate_69x13.png new file mode 100644 index 000000000..25fe93d09 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrLoadTemplate_69x13.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrReadFailed_71x20.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrReadFailed_71x20.png new file mode 100644 index 000000000..bf7c5ac26 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrReadFailed_71x20.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrRegisterDifferentTag_47x20.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrRegisterDifferentTag_47x20.png new file mode 100644 index 000000000..249ff39ab Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrRegisterDifferentTag_47x20.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrSaveCapture_65x13.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrSaveCapture_65x13.png new file mode 100644 index 000000000..ec31345de Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextErrSaveCapture_65x13.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextRegister1_71x20.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextRegister1_71x20.png new file mode 100644 index 000000000..f75495c7b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextRegister1_71x20.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextRegisterErrNotVb_71x20.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextRegisterErrNotVb_71x20.png new file mode 100644 index 000000000..eae41a325 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextRegisterErrNotVb_71x20.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextRegisterTapAgain_67x20.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextRegisterTapAgain_67x20.png new file mode 100644 index 000000000..136debf1e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextRegisterTapAgain_67x20.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextSaved_30x6.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextSaved_30x6.png new file mode 100644 index 000000000..001913d70 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextSaved_30x6.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTapApp_56x27.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTapApp_56x27.png new file mode 100644 index 000000000..7bede36c2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTapApp_56x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTransferCaptured_97x13.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTransferCaptured_97x13.png new file mode 100644 index 000000000..7ca7f6661 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTransferCaptured_97x13.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTransferDimCheck_70x20.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTransferDimCheck_70x20.png new file mode 100644 index 000000000..e510d96ea Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTransferDimCheck_70x20.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTransferSecondDimCheck_71x20.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTransferSecondDimCheck_71x20.png new file mode 100644 index 000000000..5f569b5e2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTransferSecondDimCheck_71x20.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTransfersDone_51x13.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTransfersDone_51x13.png new file mode 100644 index 000000000..1394cfc32 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/TextTransfersDone_51x13.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/WrongDevice_32x27.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/WrongDevice_32x27.png new file mode 100644 index 000000000..12ce08594 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/icons/WrongDevice_32x27.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene.c new file mode 100644 index 000000000..fcc168e28 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "vb_migrate_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const vb_migrate_on_enter_handlers[])(void*) = { +#include "vb_migrate_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const vb_migrate_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "vb_migrate_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const vb_migrate_on_exit_handlers[])(void* context) = { +#include "vb_migrate_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers vb_migrate_scene_handlers = { + .on_enter_handlers = vb_migrate_on_enter_handlers, + .on_event_handlers = vb_migrate_on_event_handlers, + .on_exit_handlers = vb_migrate_on_exit_handlers, + .scene_num = VbMigrateSceneNum, +}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene.h new file mode 100644 index 000000000..7187bdf0b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene.h @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) VbMigrateScene##id, +typedef enum { +#include "vb_migrate_scene_config.h" + VbMigrateSceneNum, +} VbMigrateScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers vb_migrate_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "vb_migrate_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "vb_migrate_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "vb_migrate_scene_config.h" +#undef ADD_SCENE diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_about.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_about.c new file mode 100644 index 000000000..fbd880dda --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_about.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +void vb_migrate_scene_about_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + FuriString* temp_str = furi_string_alloc_printf( + "\e#Information\n" + "Version: %s\n" + "Developed by: cyanic\n" + "Graphics by: Aderek\n" + "GitHub: https://github.com/GMMan/flipperzero-vb-migrate\n" + "\n" + "\e#Description\n" + "Makes transferring\n" + "characters with VB Lab/\n" + "Arena less cumbersome", + VB_MIGRATE_VERSION); + + Widget* widget = inst->widget; + vb_migrate_add_bg(widget, VbMigrateBgTypeNone); + widget_add_text_scroll_element(widget, 8, 16, 113, 33, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); +} + +bool vb_migrate_scene_about_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void vb_migrate_scene_about_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_config.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_config.h new file mode 100644 index 000000000..f090de9df --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_config.h @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +ADD_SCENE(vb_migrate, main_menu, MainMenu) +ADD_SCENE(vb_migrate, register, Register) +ADD_SCENE(vb_migrate, register_save, RegisterSave) +ADD_SCENE(vb_migrate, save_success, SaveSuccess) +ADD_SCENE(vb_migrate, select, Select) +ADD_SCENE(vb_migrate, load, Load) +ADD_SCENE(vb_migrate, info, Info) +ADD_SCENE(vb_migrate, dev_menu, DevMenu) +ADD_SCENE(vb_migrate, delete, Delete) +ADD_SCENE(vb_migrate, delete_success, DeleteSuccess) +ADD_SCENE(vb_migrate, from_app, FromApp) +ADD_SCENE(vb_migrate, to_app, ToApp) +ADD_SCENE(vb_migrate, delete_captures, DeleteCaptures) +ADD_SCENE(vb_migrate, delete_captures_success, DeleteCapturesSuccess) +ADD_SCENE(vb_migrate, about, About) diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_delete.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_delete.c new file mode 100644 index 000000000..c197cf1b8 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_delete.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" +#include "../vb_tag.h" + +static void + vb_migrate_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(inst->view_dispatcher, result); + } +} + +void vb_migrate_scene_delete_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + Widget* widget = inst->widget; + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton | VbMigrateBgTypeRightButton); + widget_add_icon_element(widget, 11, 18, &I_Delete_32x20); + widget_add_icon_element(widget, 48, 18, &I_TextDeleteVb_49x13); + + FuriString* temp_str = furi_string_alloc_printf("%d", inst->num_captured); + widget_add_string_element( + widget, 99, 24, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + widget_add_button_element( + inst->widget, GuiButtonTypeLeft, "Cancel", vb_migrate_scene_delete_widget_callback, inst); + widget_add_button_element( + inst->widget, GuiButtonTypeRight, "Delete", vb_migrate_scene_delete_widget_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); +} + +bool vb_migrate_scene_delete_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(inst->scene_manager); + } else if(event.event == GuiButtonTypeRight) { + if(vb_migrate_delete(inst, inst->text_store, true)) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneDeleteSuccess); + consumed = true; + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + inst->scene_manager, VbMigrateSceneSelect); + } + } + } + return consumed; +} + +void vb_migrate_scene_delete_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_delete_captures.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_delete_captures.c new file mode 100644 index 000000000..997141690 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_delete_captures.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +static void vb_migrate_scene_delete_captures_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(inst->view_dispatcher, result); + } +} + +void vb_migrate_scene_delete_captures_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + Widget* widget = inst->widget; + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton | VbMigrateBgTypeRightButton); + widget_add_icon_element(widget, 11, 18, &I_Delete_32x20); + widget_add_icon_element(widget, 48, 18, &I_TextClearCaptures_49x13); + + FuriString* temp_str = furi_string_alloc_printf("%d", inst->num_captured); + widget_add_string_element( + widget, 99, 24, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + widget_add_button_element( + inst->widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_delete_captures_widget_callback, + inst); + widget_add_button_element( + inst->widget, + GuiButtonTypeRight, + "Delete", + vb_migrate_scene_delete_captures_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); +} + +bool vb_migrate_scene_delete_captures_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(inst->scene_manager); + } else if(event.event == GuiButtonTypeRight) { + if(vb_migrate_delete(inst, inst->text_store, false)) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneDeleteCapturesSuccess); + consumed = true; + } else { + consumed = scene_manager_previous_scene(inst->scene_manager); + } + inst->num_captured = vb_migrate_count_captured_mons(inst, inst->text_store); + } + } + return consumed; +} + +void vb_migrate_scene_delete_captures_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_delete_captures_success.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_delete_captures_success.c new file mode 100644 index 000000000..0e52d4e9e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_delete_captures_success.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +static void vb_migrate_scene_delete_captures_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + if(result == GuiButtonTypeRight) + view_dispatcher_send_custom_event(inst->view_dispatcher, 0); + } +} + +void vb_migrate_scene_delete_captures_success_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + Widget* widget = inst->widget; + vb_migrate_add_bg(widget, VbMigrateBgTypeRightButton); + widget_add_icon_element(widget, 11, 18, &I_Delete_32x20); + widget_add_icon_element(widget, 48, 18, &I_TextCleared_47x6); + widget_add_icon_element(widget, 9, 41, &I_PulsemonRightSad_15x15); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "OK", + vb_migrate_scene_delete_captures_success_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); +} + +bool vb_migrate_scene_delete_captures_success_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom || event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + inst->scene_manager, VbMigrateSceneDevMenu); + } + return consumed; +} + +void vb_migrate_scene_delete_captures_success_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_delete_success.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_delete_success.c new file mode 100644 index 000000000..863afc777 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_delete_success.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +static void vb_migrate_scene_delete_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + VbMigrate* inst = context; + if(type == InputTypeShort) { + if(result == GuiButtonTypeRight) + + view_dispatcher_send_custom_event(inst->view_dispatcher, 0); + } +} + +void vb_migrate_scene_delete_success_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + Widget* widget = inst->widget; + vb_migrate_add_bg(widget, VbMigrateBgTypeRightButton); + widget_add_icon_element(widget, 11, 18, &I_Delete_32x20); + widget_add_icon_element(widget, 48, 18, &I_TextDeleted_46x6); + widget_add_icon_element(widget, 9, 41, &I_PulsemonRightSad_15x15); + widget_add_button_element( + widget, GuiButtonTypeRight, "OK", vb_migrate_scene_delete_success_widget_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); +} + +bool vb_migrate_scene_delete_success_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom || event.type == SceneManagerEventTypeBack) { + uint32_t back_scenes[] = {VbMigrateSceneSelect, VbMigrateSceneMainMenu}; + consumed = scene_manager_search_and_switch_to_previous_scene_one_of( + inst->scene_manager, back_scenes, COUNT_OF(back_scenes)); + } + return consumed; +} + +void vb_migrate_scene_delete_success_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_dev_menu.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_dev_menu.c new file mode 100644 index 000000000..08f82d8ee --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_dev_menu.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +typedef enum { + SubmenuDevMenuIndexTransferFromApp, + SubmenuDevMenuIndexTransferToApp, + SubmenuDevMenuClearAccountId, + SubmenuDevMenuIndexSpoof, + SubmenuDevMenuIndexClearCaptures, + SubmenuDevMenuIndexDeleteVb, + // ----- + SubmenuDevMenuClearAccountIdOff, + SubmenuDevMenuClearAccountIdOn, + // ----- + SubmenuDevMenuIndexSpoofSelection, // Always keep this last because we add tag type to it +} SubmenuDevMenuIndex; + +static void vb_migrate_scene_dev_menu_var_list_enter_callback(void* context, uint32_t index) { + VbMigrate* inst = context; + + view_dispatcher_send_custom_event(inst->view_dispatcher, index); +} + +static VbTagType + vb_migrate_scene_dev_menu_spoof_set_item_by_index(VariableItemEx* item, uint8_t index) { + VbTagType type; + if(index >= 2) { // VBC + type = (VbTagType)(index + 2); + } else { + type = (VbTagType)(index + 1); + } + + variable_item_ex_set_current_value_index(item, index); + variable_item_ex_set_current_value_text(item, vb_tag_get_tag_type_name(type)); + return type; +} + +static uint8_t + vb_migrate_scene_dev_menu_spoof_set_item_by_type(VariableItemEx* item, VbTagType type) { + uint8_t index; + if(type >= VbTagTypeVH) { + index = (uint8_t)type - 2; + } else { + index = (uint8_t)type - 1; + } + + variable_item_ex_set_current_value_index(item, index); + variable_item_ex_set_current_value_text(item, vb_tag_get_tag_type_name(type)); + return index; +} + +static void vb_migrate_scene_dev_menu_spoof_change_callback(VariableItemEx* item) { + VbMigrate* inst = variable_item_ex_get_context(item); + uint8_t index = variable_item_ex_get_current_value_index(item); + + VbTagType tag_type = vb_migrate_scene_dev_menu_spoof_set_item_by_index(item, index); + view_dispatcher_send_custom_event( + inst->view_dispatcher, SubmenuDevMenuIndexSpoofSelection + tag_type); +} + +static void vb_migrate_scene_dev_menu_clear_account_id_change_callback(VariableItemEx* item) { + VbMigrate* inst = variable_item_ex_get_context(item); + uint8_t index = variable_item_ex_get_current_value_index(item); + + variable_item_ex_set_current_value_text(item, index ? "On" : "Off"); + view_dispatcher_send_custom_event( + inst->view_dispatcher, + index ? SubmenuDevMenuClearAccountIdOn : SubmenuDevMenuClearAccountIdOff); +} + +void vb_migrate_scene_dev_menu_on_enter(void* context) { + VbMigrate* inst = context; + VariableItemListEx* variable_list = inst->variable_list; + VariableItemEx* item; + + variable_item_list_ex_set_enter_callback( + variable_list, vb_migrate_scene_dev_menu_var_list_enter_callback, inst); + variable_item_list_ex_add( + variable_list, "Transfer App > Flipper", 0, NULL, NULL, SubmenuDevMenuIndexTransferFromApp); + + if(inst->num_captured != 0) { + variable_item_list_ex_add( + variable_list, + "Transfer Flipper > App", + 0, + NULL, + NULL, + SubmenuDevMenuIndexTransferToApp); + } + + item = variable_item_list_ex_add( + variable_list, + "Unlink Account", + 2, + vb_migrate_scene_dev_menu_clear_account_id_change_callback, + inst, + SubmenuDevMenuClearAccountId); + variable_item_ex_set_current_value_index(item, inst->clear_account_id ? 1 : 0); + variable_item_ex_set_current_value_text(item, inst->clear_account_id ? "On" : "Off"); + + if(inst->orig_type == VbTagTypeVBDM || inst->orig_type == VbTagTypeVBV || + inst->orig_type == VbTagTypeVH) { + item = variable_item_list_ex_add( + variable_list, + "Spoof Version", + VbTagTypeMax - 1 - 2, // Removing VBC from list, skip VBBE + vb_migrate_scene_dev_menu_spoof_change_callback, + inst, + SubmenuDevMenuIndexSpoof); + vb_migrate_scene_dev_menu_spoof_set_item_by_type(item, inst->override_type); + } + + if(inst->num_captured != 0) { + variable_item_list_ex_add( + variable_list, "Clear Captures", 0, NULL, NULL, SubmenuDevMenuIndexClearCaptures); + } + + variable_item_list_ex_add( + variable_list, "Delete Vital Bracelet", 0, NULL, NULL, SubmenuDevMenuIndexDeleteVb); + + // variable_item_list_ex_set_selected_item( + // variable_list, scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneDevMenu)); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewVariableItemList); +} + +bool vb_migrate_scene_dev_menu_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + // if(event.event < SubmenuDevMenuIndexSpoofSelection) { + // scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneDevMenu, event.event); + // } + + if(event.event == SubmenuDevMenuIndexTransferFromApp) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneFromApp); + consumed = true; + } else if(event.event == SubmenuDevMenuIndexTransferToApp) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneToApp); + consumed = true; + } else if(event.event == SubmenuDevMenuIndexClearCaptures) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneDeleteCaptures); + consumed = true; + } else if(event.event == SubmenuDevMenuIndexDeleteVb) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneDelete); + consumed = true; + } else if(event.event == SubmenuDevMenuClearAccountIdOff) { + inst->clear_account_id = false; + consumed = true; + } else if(event.event == SubmenuDevMenuClearAccountIdOn) { + inst->clear_account_id = true; + consumed = true; + } else if(event.event >= SubmenuDevMenuIndexSpoofSelection) { + // Note: skipping SubmenuDevMenuIndexSpoof because there's nothing to do on enter + inst->override_type = event.event - SubmenuDevMenuIndexSpoofSelection; + consumed = true; + } + } + return consumed; +} + +void vb_migrate_scene_dev_menu_on_exit(void* context) { + VbMigrate* inst = context; + + variable_item_list_ex_reset(inst->variable_list); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_from_app.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_from_app.c new file mode 100644 index 000000000..7b4eaa012 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_from_app.c @@ -0,0 +1,370 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "../vb_migrate_i.h" +#include "../vb_tag.h" + +#define TAG "vb_migrate_scene_from_app" + +typedef enum { + FromAppStateInitial, + FromAppStateInstructions, + FromAppStateEmulateReady, + FromAppStateEmulateCheckDim, + FromAppStateEmulateTransferFromApp, + FromAppStateTemplateError, + FromAppStateSaveError, +} FromAppState; + +typedef enum { + FromAppEventTypeWidgetLeft, + FromAppEventTypeWidgetRight, + FromAppEventTypeTemplateLoadError, + FromAppEventTypeTagWrite, + FromAppEventTypeCaptureSaveError, + FromAppEventTypeCaptureSaveSuccess, +} FromAppEventType; + +static void + vb_migrate_scene_from_app_widget_callback(GuiButtonType result, InputType type, void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + if(result == GuiButtonTypeLeft) + view_dispatcher_send_custom_event(inst->view_dispatcher, FromAppEventTypeWidgetLeft); + else if(result == GuiButtonTypeRight) + view_dispatcher_send_custom_event(inst->view_dispatcher, FromAppEventTypeWidgetRight); + } +} + +static bool vb_migrate_scene_from_app_worker_callback(NfcWorkerEvent event, void* context) { + VbMigrate* inst = context; + bool result = false; + + if(event == NfcWorkerEventSuccess) { + view_dispatcher_send_custom_event(inst->view_dispatcher, FromAppEventTypeTagWrite); + result = true; + } + + return result; +} + +static void vb_migrate_scene_from_app_set_nfc_state(VbMigrate* inst, FromAppState state) { + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + if(state == FromAppStateEmulateReady) { + vb_tag_set_random_nonce(bant); + vb_tag_set_status(bant, VbTagStatusReady); + vb_tag_set_operation(bant, VbTagOperationReady); + } else if(state == FromAppStateEmulateCheckDim) { + vb_tag_set_status(bant, VbTagStatusReady | VbTagStatusDimReady); + vb_tag_set_operation(bant, VbTagOperationReady); + } else if(state == FromAppStateEmulateTransferFromApp) { + vb_tag_set_status(bant, 0); + vb_tag_set_operation(bant, VbTagOperationIdle); + } + + // Override tag type + if(inst->override_type != inst->orig_type && inst->override_type != VbTagTypeUnknown) { + vb_tag_set_item_id_no(bant, vb_tag_get_default_product(inst->override_type)); + } + + if(inst->clear_account_id) { + vb_tag_set_app_flag(bant, false); + } +} + +static bool vb_migrate_scene_from_app_is_state_changed(VbMigrate* inst, FromAppState state) { + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + VbTagOperation operation = vb_tag_get_operation(bant); + + if(state == FromAppStateEmulateReady) { + return operation == VbTagOperationCheckDim; + } else if(state == FromAppStateEmulateCheckDim) { + return operation == VbTagOperationCheckDim || operation == VbTagOperationReturnFromApp; + } + + return false; +} + +static void vb_migrate_scene_from_app_set_state(VbMigrate* inst, FromAppState state) { + uint32_t curr_state = + scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneFromApp); + if(state != curr_state) { + Widget* widget = inst->widget; + + if(state == FromAppStateInstructions) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton | VbMigrateBgTypeRightButton); + widget_add_text_scroll_element( + widget, + 8, + 16, + 113, + 33, + "\e#To transfer\n" + "\e#characters to Flipper:\n" + "0. If on VB Arena, select\n" + "the device type that\n" + "matches your current\n" + "settings\n" + "1. If on VB Lab, wake up\n" + "character from storage\n" + "2. Sync character to\n" + "Flipper as if it was a\n" + "Vital Bracelet. Flipper\n" + "will beep when it is\n" + "ready for the next step\n" + "3. The character is\n" + "automatically saved when\n" + "the transfer is complete,\n" + "and will be ready for\n" + "another transfer\n" + "4. Repeat the above until\n" + "you have transferred all\n" + "the characters you want\n" + "\n" + "You can cancel at any\n" + "time to finish transferring."); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_from_app_widget_callback, + inst); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Next", + vb_migrate_scene_from_app_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + } else if(state == FromAppStateEmulateReady) { + vb_migrate_show_loading_popup(inst, true); + if(vb_migrate_load_nfc(inst, inst->text_store, VB_MIGRATE_TEMPLATE_NAME)) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton); + widget_add_icon_element(widget, 11, 18, &I_CommReady_32x27); + widget_add_icon_element(widget, 48, 18, &I_TextTransferDimCheck_70x20); + FuriString* temp_str = furi_string_alloc_printf("%d", inst->num_captured); + widget_add_string_element( + widget, + 100, + 31, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(temp_str)); + widget_add_icon_element(widget, 106, 40, &I_PulsemonRightWaiting_15x16); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_from_app_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + furi_string_free(temp_str); + + vb_migrate_scene_from_app_set_nfc_state(inst, state); + nfc_worker_start( + inst->worker, + NfcWorkerStateMfUltralightEmulate, + &inst->nfc_dev->dev_data, + vb_migrate_scene_from_app_worker_callback, + inst); + vb_migrate_blink_emulate(inst); + } else { + view_dispatcher_send_custom_event( + inst->view_dispatcher, FromAppEventTypeTemplateLoadError); + } + vb_migrate_show_loading_popup(inst, false); + } else if(state == FromAppStateEmulateCheckDim) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton); + widget_add_icon_element(widget, 11, 18, &I_CommReady_32x27); + widget_add_icon_element(widget, 48, 18, &I_TextTransferSecondDimCheck_71x20); + widget_add_icon_element(widget, 106, 40, &I_PulsemonRightWaiting_15x16); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_from_app_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + notification_message(inst->notifications, &sequence_success); + vb_migrate_scene_from_app_set_nfc_state(inst, state); + nfc_worker_start( + inst->worker, + NfcWorkerStateMfUltralightEmulate, + &inst->nfc_dev->dev_data, + vb_migrate_scene_from_app_worker_callback, + inst); + vb_migrate_blink_emulate(inst); + } else if(state == FromAppStateEmulateTransferFromApp) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton); + widget_add_icon_element(widget, 15, 18, &I_TextTransferCaptured_97x13); + widget_add_icon_element(widget, 106, 40, &I_PulsemonRightWaiting_15x16); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_from_app_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + nfc_worker_stop(inst->worker); + vb_migrate_blink_stop(inst); + vb_migrate_scene_from_app_set_nfc_state(inst, state); + notification_message(inst->notifications, &sequence_success); + + // Restore original tag type if necessary + if(inst->override_type != inst->orig_type && inst->override_type != VbTagTypeUnknown) { + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + vb_tag_set_item_id_no(bant, inst->orig_product); + } + + // Save the tag + inst->next_id = vb_migrate_get_next_id(inst, inst->text_store, inst->next_id, false); + FuriString* save_path = furi_string_alloc_printf( + VB_MIGRATE_CAPTURE_FORMAT, inst->next_id, NFC_APP_EXTENSION); + if(vb_migrate_save_nfc(inst, inst->text_store, furi_string_get_cstr(save_path))) { + view_dispatcher_send_custom_event( + inst->view_dispatcher, FromAppEventTypeCaptureSaveSuccess); + } else { + view_dispatcher_send_custom_event( + inst->view_dispatcher, FromAppEventTypeCaptureSaveError); + } + furi_string_free(save_path); + } else if(state == FromAppStateTemplateError) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton); + widget_add_icon_element(widget, 11, 18, &I_WrongDevice_32x27); + widget_add_icon_element(widget, 48, 18, &I_TextErrLoadTemplate_69x13); + widget_add_icon_element(widget, 104, 41, &I_PulsemonLeftWait_15x15); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_from_app_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + notification_message(inst->notifications, &sequence_error); + notification_message(inst->notifications, &sequence_set_red_255); + } else if(state == FromAppStateSaveError) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton); + widget_add_icon_element(widget, 11, 18, &I_WrongDevice_32x27); + widget_add_icon_element(widget, 48, 18, &I_TextErrSaveCapture_65x13); + widget_add_icon_element(widget, 104, 41, &I_PulsemonLeftWait_15x15); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_from_app_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + notification_message(inst->notifications, &sequence_error); + notification_message(inst->notifications, &sequence_set_red_255); + } else { + furi_crash("Unknown new state in vb_migrate_scene_from_app_set_state"); + } + + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneFromApp, state); + } +} + +void vb_migrate_scene_from_app_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + inst->next_id = 0; + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneFromApp, FromAppStateInitial); + vb_migrate_scene_from_app_set_state(inst, FromAppStateInstructions); +} + +bool vb_migrate_scene_from_app_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == FromAppEventTypeWidgetLeft) { + consumed = scene_manager_previous_scene(inst->scene_manager); + } else if(event.event == FromAppEventTypeWidgetRight) { + vb_migrate_scene_from_app_set_state(inst, FromAppStateEmulateReady); + consumed = true; + } else if(event.event == FromAppEventTypeTagWrite) { + uint32_t state = + scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneFromApp); + if(vb_migrate_scene_from_app_is_state_changed(inst, state)) { + if(state == FromAppStateEmulateReady) { + nfc_worker_stop(inst->worker); + vb_migrate_blink_stop(inst); + vb_migrate_scene_from_app_set_state(inst, FromAppStateEmulateCheckDim); + consumed = true; + } else if(state == FromAppStateEmulateCheckDim) { + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + VbTagOperation operation = vb_tag_get_operation(bant); + + if(operation == VbTagOperationReturnFromApp) { + nfc_worker_stop(inst->worker); + vb_migrate_blink_stop(inst); + vb_migrate_scene_from_app_set_state( + inst, FromAppStateEmulateTransferFromApp); + consumed = true; + } else if(operation == VbTagOperationCheckDim) { + // Don't need to reset tag, but should make a beep + vb_migrate_blink_stop(inst); + notification_message_block(inst->notifications, &sequence_success); + vb_migrate_blink_emulate(inst); + } + } + } + } else if(event.event == FromAppEventTypeTemplateLoadError) { + vb_migrate_scene_from_app_set_state(inst, FromAppStateTemplateError); + consumed = true; + } else if(event.event == FromAppEventTypeCaptureSaveError) { + vb_migrate_scene_from_app_set_state(inst, FromAppStateSaveError); + consumed = true; + } else if(event.event == FromAppEventTypeCaptureSaveSuccess) { + ++inst->num_captured; + ++inst->next_id; + vb_migrate_scene_from_app_set_state(inst, FromAppStateEmulateReady); + consumed = true; + } else { + furi_crash("Unknown event in vb_migrate_scene_from_app_on_event"); + } + } + return consumed; +} + +void vb_migrate_scene_from_app_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); + nfc_worker_stop(inst->worker); + vb_migrate_blink_stop(inst); + notification_message_block(inst->notifications, &sequence_reset_red); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_info.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_info.c new file mode 100644 index 000000000..f5a991a56 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_info.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" +#include "../vb_tag.h" + +static void + vb_migrate_scene_info_button_callback(GuiButtonType result, InputType type, void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + if(result == GuiButtonTypeRight) { + view_dispatcher_send_custom_event(inst->view_dispatcher, result); + } + } +} + +void vb_migrate_scene_info_on_enter(void* context) { + VbMigrate* inst = context; + FuriString* temp_str = furi_string_alloc(); + + // Build info scroll + // Name + furi_string_cat_printf(temp_str, "\ec%s\n", inst->text_store); + + // Type + if(inst->orig_product == NULL) + furi_string_cat(temp_str, "Unknown product\n"); + else + furi_string_cat_printf(temp_str, "\e#%s\n", inst->orig_product->name); + + // Number of mons loaded + furi_string_cat_printf(temp_str, "Charas. captured: %d", inst->num_captured); + + vb_migrate_add_bg(inst->widget, VbMigrateBgTypeRightButton); + + widget_add_text_scroll_element(inst->widget, 8, 16, 113, 33, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + widget_add_button_element( + inst->widget, GuiButtonTypeRight, "More", vb_migrate_scene_info_button_callback, inst); + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); +} + +bool vb_migrate_scene_info_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + UNUSED(inst); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + // Reset menu selection index + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneDevMenu, 0); + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneDevMenu); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + uint32_t back_scenes[] = {VbMigrateSceneSelect, VbMigrateSceneMainMenu}; + consumed = scene_manager_search_and_switch_to_previous_scene_one_of( + inst->scene_manager, back_scenes, COUNT_OF(back_scenes)); + } + return consumed; +} + +void vb_migrate_scene_info_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_load.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_load.c new file mode 100644 index 000000000..8f316a1ed --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_load.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +#define TAG "vb_migrate_scene_load" + +void vb_migrate_scene_load_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + vb_migrate_show_loading_popup(inst, true); + view_dispatcher_send_custom_event(inst->view_dispatcher, 0); +} + +bool vb_migrate_scene_load_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(vb_migrate_load_nfc(inst, inst->text_store, VB_MIGRATE_TEMPLATE_NAME)) { + inst->num_captured = vb_migrate_count_captured_mons(inst, inst->text_store); + + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + const VbTagProduct* product = vb_tag_find_product(bant); + inst->orig_product = product; + if(product) { + inst->orig_type = product->type; + } else { + inst->orig_type = VbTagTypeUnknown; + } + inst->override_type = inst->orig_type; + + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneInfo); + consumed = true; + } else { + consumed = scene_manager_previous_scene(inst->scene_manager); + } + vb_migrate_show_loading_popup(inst, false); + } + return consumed; +} + +void vb_migrate_scene_load_on_exit(void* context) { + UNUSED(context); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_main_menu.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_main_menu.c new file mode 100644 index 000000000..13dea042d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_main_menu.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +typedef enum { + SubmenuMainIndexRegister, + SubmenuMainIndexSelect, + SubmenuMainIndexAbout, +} SubmenuMainIndex; + +static void vb_migrate_scene_main_menu_submenu_callback(void* context, uint32_t index) { + VbMigrate* inst = context; + + view_dispatcher_send_custom_event(inst->view_dispatcher, index); +} + +void vb_migrate_scene_main_menu_on_enter(void* context) { + VbMigrate* inst = context; + Submenu* submenu = inst->submenu; + + submenu_add_item( + submenu, + "Register Vital Bracelet", + SubmenuMainIndexRegister, + vb_migrate_scene_main_menu_submenu_callback, + inst); + submenu_add_item( + submenu, + "Select Vital Bracelet", + SubmenuMainIndexSelect, + vb_migrate_scene_main_menu_submenu_callback, + inst); + submenu_add_item( + submenu, "About", SubmenuMainIndexAbout, vb_migrate_scene_main_menu_submenu_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewMenu); +} + +bool vb_migrate_scene_main_menu_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuMainIndexRegister) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneRegister); + consumed = true; + } else if(event.event == SubmenuMainIndexSelect) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneSelect); + consumed = true; + } else if(event.event == SubmenuMainIndexAbout) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneAbout); + consumed = true; + } + } + return consumed; +} + +void vb_migrate_scene_main_menu_on_exit(void* context) { + VbMigrate* inst = context; + + submenu_reset(inst->submenu); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_register.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_register.c new file mode 100644 index 000000000..ce4c05008 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_register.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "../vb_tag.h" +#include "../vb_migrate_i.h" + +typedef enum { + RegisterStateInitial, + RegisterStateInstructionInitial, + RegisterStateInstructionConnect, + RegisterStateCaptureInitial, + RegisterStateCaptureInvalidTag, + RegisterStateCapturePwd, + RegisterStateCaptureFull, + RegisterStateCaptureFailed, + RegisterStateCaptureIncorrectTag, +} RegisterState; + +typedef enum { + RegisterEventTypeNextButton, + RegisterEventTypePrevButton, + RegisterEventTypeVbReadInitial, + RegisterEventTypeVbPwdAuth, + RegisterEventTypeVbReadFullSuccess, + RegisterEventTypeVbReadFullFail, +} RegisterEventType; + +static void + vb_migrate_scene_register_widget_callback(GuiButtonType result, InputType type, void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + if(result == GuiButtonTypeRight) + view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypeNextButton); + else if(result == GuiButtonTypeLeft) + view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypePrevButton); + } +} + +static bool + vb_migrate_scene_register_worker_read_initial_callback(NfcWorkerEvent event, void* context) { + VbMigrate* inst = context; + bool consumed = false; + + if(event == NfcWorkerEventReadMfUltralight) { + view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypeVbReadInitial); + consumed = true; + } + + return consumed; +} + +static bool vb_migrate_scene_register_worker_auth_callback(NfcWorkerEvent event, void* context) { + VbMigrate* inst = context; + bool consumed = false; + + if(event == NfcWorkerEventMfUltralightPwdAuth) { + view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypeVbPwdAuth); + consumed = true; + } + + return consumed; +} + +static bool + vb_migrate_scene_register_worker_full_capture_callback(NfcWorkerEvent event, void* context) { + VbMigrate* inst = context; + bool consumed = false; + + if(event == NfcWorkerEventMfUltralightPassKey) { + memcpy( + inst->nfc_dev->dev_data.mf_ul_data.auth_key, + inst->captured_pwd, + sizeof(inst->captured_pwd)); + consumed = true; + } else if(event == NfcWorkerEventSuccess) { + view_dispatcher_send_custom_event( + inst->view_dispatcher, RegisterEventTypeVbReadFullSuccess); + consumed = true; + } else if(event == NfcWorkerEventFail) { + view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterEventTypeVbReadFullFail); + consumed = true; + } + + return consumed; +} + +static void vb_migrate_scene_register_cleanup_state(VbMigrate* inst, RegisterState state) { + if(state == RegisterStateCaptureInvalidTag || state == RegisterStateCaptureFailed || + state == RegisterStateCaptureIncorrectTag) { + notification_message(inst->notifications, &sequence_reset_red); + } else if( + state == RegisterStateCaptureInitial || state == RegisterStateCapturePwd || + state == RegisterStateCaptureFull) { + vb_migrate_blink_stop(inst); + nfc_worker_stop(inst->worker); + } +} + +static void vb_migrate_scene_register_set_state(VbMigrate* inst, RegisterState state) { + uint32_t curr_state = + scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneRegister); + if(state != curr_state) { + vb_migrate_scene_register_cleanup_state(inst, curr_state); + Widget* widget = inst->widget; + + if(state == RegisterStateInstructionInitial) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton | VbMigrateBgTypeRightButton); + widget_add_text_scroll_element( + widget, + 8, + 16, + 113, + 33, + "Please make sure your\n" + "current character has\n" + "been sent to VB Lab/\n" + "Arena before\n" + "continuing."); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_register_widget_callback, + inst); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Next", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + } else if(state == RegisterStateInstructionConnect) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton | VbMigrateBgTypeRightButton); + widget_add_text_scroll_element( + widget, + 8, + 16, + 113, + 33, + "Prepare VB Lab:\n" + "1. Ensure current\n" + "character in VB Lab has\n" + "been put in storage\n" + "2. Open the \"Scan\" screen\n" + "3. Tap \"Vital Bracelet ->\n" + "App\"\n" + "\n" + "Prepare VB Arena:\n" + "1. Select the device type\n" + "that matches your VB\n" + "2. Tap the \"Send\" button"); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Cancel", + vb_migrate_scene_register_widget_callback, + inst); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Next", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + } else if(state == RegisterStateCaptureInitial) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton); + widget_add_icon_element(widget, 11, 25, &I_CommVB_32x20); + widget_add_icon_element(widget, 21, 17, &I_CommWave_12x8); + widget_add_icon_element(widget, 48, 18, &I_TextRegister1_71x20); + widget_add_icon_element(widget, 105, 40, &I_PulsemonLeftStand_14x16); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Previous", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + nfc_device_clear(inst->nfc_dev); + inst->nfc_dev->dev_data.read_mode = NfcReadModeMfUltralight; + nfc_worker_start( + inst->worker, + NfcWorkerStateRead, + &inst->nfc_dev->dev_data, + vb_migrate_scene_register_worker_read_initial_callback, + inst); + vb_migrate_blink_read(inst); + } else if(state == RegisterStateCaptureInvalidTag) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton); + widget_add_icon_element(widget, 11, 18, &I_WrongDevice_32x27); + widget_add_icon_element(widget, 48, 18, &I_TextRegisterErrNotVb_71x20); + widget_add_icon_element(widget, 104, 41, &I_PulsemonLeftWait_15x15); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + notification_message(inst->notifications, &sequence_set_red_255); + } else if(state == RegisterStateCapturePwd) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton); + widget_add_icon_element(widget, 11, 18, &I_AppInteract_32x27); + widget_add_icon_element(widget, 48, 18, &I_TextTapApp_56x27); + widget_add_icon_element(widget, 105, 40, &I_PulsemonLeftStand_14x16); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Previous", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + vb_tag_set_operation(bant, VbTagOperationReady); + vb_tag_set_status(bant, VbTagStatusReady); + nfc_worker_start( + inst->worker, + NfcWorkerStateMfUltralightEmulate, + &inst->nfc_dev->dev_data, + vb_migrate_scene_register_worker_auth_callback, + inst); + vb_migrate_blink_emulate(inst); + } else if(state == RegisterStateCaptureFull) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton); + widget_add_icon_element(widget, 11, 25, &I_CommVB_32x20); + widget_add_icon_element(widget, 23, 18, &I_CommReturn_10x8); + widget_add_icon_element(widget, 48, 18, &I_TextRegisterTapAgain_67x20); + widget_add_icon_element(widget, 104, 41, &I_PulsemonLeftWait_15x15); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Previous", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + + inst->nfc_dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAuto; + nfc_worker_start( + inst->worker, + NfcWorkerStateReadMfUltralightReadAuth, + &inst->nfc_dev->dev_data, + vb_migrate_scene_register_worker_full_capture_callback, + inst); + vb_migrate_blink_read(inst); + } else if(state == RegisterStateCaptureFailed) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton); + widget_add_icon_element(widget, 11, 18, &I_WrongDevice_32x27); + widget_add_icon_element(widget, 48, 18, &I_TextErrReadFailed_71x20); + widget_add_icon_element(widget, 104, 41, &I_PulsemonLeftWait_15x15); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + notification_message(inst->notifications, &sequence_set_red_255); + } else if(state == RegisterStateCaptureIncorrectTag) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton); + widget_add_icon_element(widget, 11, 18, &I_WrongDevice_32x27); + widget_add_icon_element(widget, 48, 18, &I_TextErrRegisterDifferentTag_47x20); + widget_add_icon_element(widget, 104, 41, &I_PulsemonLeftWait_15x15); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + vb_migrate_scene_register_widget_callback, + inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + notification_message(inst->notifications, &sequence_set_red_255); + } else { + furi_crash("Unknown new state in vb_migrate_scene_register_set_state"); + } + + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneRegister, state); + } +} + +static bool vb_migrate_scene_register_next_state(VbMigrate* inst, RegisterState state) { + if(state == RegisterStateInstructionInitial) { + vb_migrate_scene_register_set_state(inst, RegisterStateInstructionConnect); + return true; + } else if(state == RegisterStateInstructionConnect) { + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureInitial); + return true; + } + + return false; +} + +static bool + vb_migrate_scene_register_prev_state(VbMigrate* inst, RegisterState state, bool is_back) { + UNUSED(is_back); + + if(state == RegisterStateInstructionInitial || state == RegisterStateInstructionConnect) { + return scene_manager_previous_scene(inst->scene_manager); + } else if(state == RegisterStateCaptureInitial) { + vb_migrate_scene_register_set_state(inst, RegisterStateInstructionConnect); + return true; + } else if( + state == RegisterStateCaptureInvalidTag || state == RegisterStateCapturePwd || + state == RegisterStateCaptureFull) { + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureInitial); + return true; + } else if(state == RegisterStateCaptureFailed || state == RegisterStateCaptureIncorrectTag) { + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureFull); + return true; + } + + return is_back; +} + +void vb_migrate_scene_register_on_enter(void* context) { + VbMigrate* inst = context; + + scene_manager_set_scene_state( + inst->scene_manager, VbMigrateSceneRegister, RegisterStateInitial); + vb_migrate_scene_register_set_state(inst, RegisterStateInstructionInitial); +} + +bool vb_migrate_scene_register_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + RegisterState state = + scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneRegister); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == RegisterEventTypeNextButton) { + consumed = vb_migrate_scene_register_next_state(inst, state); + } else if(event.event == RegisterEventTypePrevButton) { + consumed = vb_migrate_scene_register_prev_state(inst, state, false); + } else if(event.event == RegisterEventTypeVbReadInitial) { + if(vb_tag_validate_product(&inst->nfc_dev->dev_data)) { + memcpy( + inst->captured_uid, + inst->nfc_dev->dev_data.nfc_data.uid, + sizeof(inst->captured_uid)); + notification_message(inst->notifications, &sequence_success); + vb_migrate_scene_register_set_state(inst, RegisterStateCapturePwd); + } else { + notification_message(inst->notifications, &sequence_error); + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureInvalidTag); + } + consumed = true; + } else if(event.event == RegisterEventTypeVbPwdAuth) { + // Set up for auth + memcpy( + inst->captured_pwd, + inst->nfc_dev->dev_data.mf_ul_auth.pwd.raw, + sizeof(inst->captured_pwd)); + + notification_message(inst->notifications, &sequence_success); + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureFull); + consumed = true; + } else if(event.event == RegisterEventTypeVbReadFullSuccess) { + NfcDeviceData* dev_data = &inst->nfc_dev->dev_data; + if(memcmp(dev_data->nfc_data.uid, inst->captured_uid, sizeof(inst->captured_uid)) || + dev_data->mf_ul_data.data_read != dev_data->mf_ul_data.data_size) { + notification_message(inst->notifications, &sequence_error); + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureIncorrectTag); + } else { + notification_message(inst->notifications, &sequence_success); + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneRegisterSave); + } + consumed = true; + } else if(event.event == RegisterEventTypeVbReadFullFail) { + notification_message(inst->notifications, &sequence_error); + vb_migrate_scene_register_set_state(inst, RegisterStateCaptureFailed); + consumed = true; + } else { + furi_crash("Unknown event in vb_migrate_scene_register_on_event"); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = vb_migrate_scene_register_prev_state(inst, state, true); + } + return consumed; +} + +void vb_migrate_scene_register_on_exit(void* context) { + VbMigrate* inst = context; + RegisterState state = + scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneRegister); + + vb_migrate_scene_register_cleanup_state(inst, state); + widget_reset(inst->widget); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_register_save.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_register_save.c new file mode 100644 index 000000000..e388ed25e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_register_save.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "../vb_migrate_i.h" +#include "../vb_tag.h" + +// This thing doesn't know what a FuriString is +#include + +typedef enum { + RegisterSaveEventTextInput, +} RegisterSaveEvent; + +static void vb_migrate_scene_register_save_text_input_callback(void* context) { + VbMigrate* inst = context; + + view_dispatcher_send_custom_event(inst->view_dispatcher, RegisterSaveEventTextInput); +} + +void vb_migrate_scene_register_save_on_enter(void* context) { + VbMigrate* inst = context; + + // Setup view + TextInput* text_input = inst->text_input; + text_input_set_header_text(text_input, "Name this Vital Bracelet"); + text_input_set_result_callback( + text_input, + vb_migrate_scene_register_save_text_input_callback, + inst, + inst->text_store, + VB_MIGRATE_MAX_DEV_NAME_LENGTH, + true); + + // Set default name + FuriString* temp_str; + NfcDeviceData* dev_data = &inst->nfc_dev->dev_data; + BantBlock* bant = vb_tag_get_bant_block(dev_data); + const VbTagProduct* prod = vb_tag_find_product(bant); + temp_str = furi_string_alloc_printf("%s_", prod->short_name); + for(size_t i = 0; i < dev_data->nfc_data.uid_len; ++i) { + furi_string_cat_printf(temp_str, "%02x", dev_data->nfc_data.uid[i]); + } + vb_migrate_text_store_set(inst, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + // We're validating whether folder exists + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(VB_MIGRATE_FOLDER, "/" VB_MIGRATE_TEMPLATE_NAME, NULL); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewTextInput); + notification_message(inst->notifications, &sequence_set_green_255); +} + +bool vb_migrate_scene_register_save_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == RegisterSaveEventTextInput) { + if(strlen(inst->text_store) != 0) { + vb_migrate_show_loading_popup(inst, true); + if(vb_migrate_save_nfc(inst, inst->text_store, VB_MIGRATE_TEMPLATE_NAME)) { + inst->num_captured = 0; + + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + const VbTagProduct* product = vb_tag_find_product(bant); + inst->orig_product = product; + if(product) { + inst->orig_type = product->type; + } else { + inst->orig_type = VbTagTypeUnknown; + } + inst->override_type = inst->orig_type; + + // Go to success + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneSaveSuccess); + } else { + // Otherwise just stay here + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewTextInput); + } + vb_migrate_show_loading_popup(inst, false); + + consumed = true; + } + } + } + return consumed; +} + +void vb_migrate_scene_register_save_on_exit(void* context) { + VbMigrate* inst = context; + + // Clear view + TextInput* text_input = inst->text_input; + ValidatorIsFile* validator = text_input_get_validator_callback_context(text_input); + text_input_set_validator(text_input, NULL, NULL); + validator_is_file_free(validator); + + text_input_reset(text_input); + notification_message_block(inst->notifications, &sequence_reset_green); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_save_success.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_save_success.c new file mode 100644 index 000000000..f62b4d24c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_save_success.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +static void vb_migrate_scene_save_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + if(result == GuiButtonTypeRight) + view_dispatcher_send_custom_event(inst->view_dispatcher, 0); + } +} + +void vb_migrate_scene_save_success_on_enter(void* context) { + VbMigrate* inst = context; + + // Setup view + Widget* widget = inst->widget; + vb_migrate_add_bg(widget, VbMigrateBgTypeRightButton); + widget_add_icon_element(widget, 11, 18, &I_CommComplete_32x20); + widget_add_icon_element(widget, 48, 18, &I_TextSaved_30x6); + widget_add_icon_element(widget, 9, 40, &I_PulsemonRightHappy_14x16); + widget_add_button_element( + widget, GuiButtonTypeRight, "OK", vb_migrate_scene_save_success_widget_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); +} + +bool vb_migrate_scene_save_success_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom || event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneInfo); + consumed = true; + } + return consumed; +} + +void vb_migrate_scene_save_success_on_exit(void* context) { + VbMigrate* inst = context; + + // Clear view + widget_reset(inst->widget); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_select.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_select.c new file mode 100644 index 000000000..84f8590d8 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_select.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../vb_migrate_i.h" + +static void vb_migrate_scene_select_file_select_callback(bool result, void* context) { + VbMigrate* inst = context; + + view_dispatcher_send_custom_event(inst->view_dispatcher, result); +} + +void vb_migrate_scene_select_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + file_select_set_callback( + inst->file_select, vb_migrate_scene_select_file_select_callback, inst); + file_select_set_filter(inst->file_select, VB_MIGRATE_FOLDER, "*"); + file_select_set_result_buffer(inst->file_select, inst->text_store, sizeof(inst->text_store)); + file_select_init(inst->file_select); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewFileSelect); +} + +bool vb_migrate_scene_select_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + bool consumed = false; + UNUSED(inst); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event) { + // File selected + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneLoad); + consumed = true; + } + } + return consumed; +} + +void vb_migrate_scene_select_on_exit(void* context) { + UNUSED(context); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_to_app.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_to_app.c new file mode 100644 index 000000000..08941332a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/scenes/vb_migrate_scene_to_app.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "../vb_migrate_i.h" +#include "../vb_tag.h" + +#define TAG "vb_migrate_scene_to_app" + +typedef enum { + ToAppStateInitial, + ToAppStateInstructions, + ToAppStateEmulateReady, + ToAppStateEmulateTransferToApp, + ToAppStateLoadError, + ToAppStateComplete, +} ToAppState; + +typedef enum { + ToAppEventTypeWidgetLeft, + ToAppEventTypeWidgetRight, + ToAppEventTypeEmulateStart, + ToAppEventTypeCaptureLoadError, + ToAppEventTypeTagWrite, +} ToAppEventType; + +static void + vb_migrate_scene_to_app_widget_callback(GuiButtonType result, InputType type, void* context) { + VbMigrate* inst = context; + + if(type == InputTypeShort) { + if(result == GuiButtonTypeLeft) + view_dispatcher_send_custom_event(inst->view_dispatcher, ToAppEventTypeWidgetLeft); + else if(result == GuiButtonTypeRight) + view_dispatcher_send_custom_event(inst->view_dispatcher, ToAppEventTypeWidgetRight); + } +} + +static bool vb_migrate_scene_to_app_worker_callback(NfcWorkerEvent event, void* context) { + VbMigrate* inst = context; + bool result = false; + + if(event == NfcWorkerEventSuccess) { + view_dispatcher_send_custom_event(inst->view_dispatcher, ToAppEventTypeTagWrite); + result = true; + } + + return result; +} + +static void vb_migrate_scene_to_app_set_nfc_state(VbMigrate* inst, ToAppState state) { + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + if(state == ToAppStateEmulateReady) { + vb_tag_set_random_nonce(bant); + vb_tag_set_status(bant, VbTagStatusReady); + vb_tag_set_operation(bant, VbTagOperationReady); + } + + // Override tag type + if(inst->override_type != inst->orig_type && inst->override_type != VbTagTypeUnknown) { + vb_tag_set_item_id_no(bant, vb_tag_get_default_product(inst->override_type)); + } + + if(inst->clear_account_id) { + vb_tag_set_app_flag(bant, false); + } +} + +static bool vb_migrate_scene_to_app_is_state_changed(VbMigrate* inst, ToAppState state) { + BantBlock* bant = vb_tag_get_bant_block(&inst->nfc_dev->dev_data); + VbTagOperation operation = vb_tag_get_operation(bant); + + if(state == ToAppStateEmulateReady) { + return operation == VbTagOperationTransferToApp; + } + + return false; +} + +static void vb_migrate_scene_to_app_set_state(VbMigrate* inst, ToAppState state) { + uint32_t curr_state = scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneToApp); + if(state != curr_state) { + Widget* widget = inst->widget; + + if(state == ToAppStateInstructions) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton | VbMigrateBgTypeRightButton); + widget_add_text_scroll_element( + widget, + 8, + 16, + 113, + 33, + "\e#To transfer\n" + "\e#characters from\n" + "\e#Flipper:\n" + "0. If on VB Arena, select\n" + "the device type that\n" + "matches your current\n" + "settings and the correct\n" + "franchise for the\n" + "characters you're\n" + "transferring\n" + "1. If on VB Lab, put the\n" + "current character into\n" + "storage\n" + "2. Sync character from\n" + "Flipper as if it was a Vital\n" + "Bracelet. Flipper will beep\n" + "and automatically\n" + "advance to the next\n" + "captured character when\n" + "VB Lab/Arena has\n" + "transferred the current\n" + "character\n" + "\n" + "-> You can press the right\n" + "key to skip sending the\n" + "current character\n" + "3. Repeat the above until\n" + "you have transferred all\n" + "the characters you want\n" + "\n" + "You can cancel at any\n" + "time to finish transferring."); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Cancel", vb_migrate_scene_to_app_widget_callback, inst); + widget_add_button_element( + widget, GuiButtonTypeRight, "Next", vb_migrate_scene_to_app_widget_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + } else if(state == ToAppStateEmulateReady) { + view_dispatcher_send_custom_event(inst->view_dispatcher, ToAppEventTypeEmulateStart); + } else if(state == ToAppStateLoadError) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton); + widget_add_icon_element(widget, 11, 18, &I_WrongDevice_32x27); + widget_add_icon_element(widget, 48, 18, &I_TextErrLoadCapture_65x13); + widget_add_icon_element(widget, 104, 41, &I_PulsemonLeftWait_15x15); + FuriString* temp_str = furi_string_alloc_printf("%03d", inst->next_id); + widget_add_string_multiline_element( + widget, 48, 32, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Cancel", vb_migrate_scene_to_app_widget_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + furi_string_free(temp_str); + notification_message(inst->notifications, &sequence_error); + notification_message(inst->notifications, &sequence_set_red_255); + } else if(state == ToAppStateComplete) { + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeRightButton); + widget_add_icon_element(widget, 11, 18, &I_CommComplete_32x20); + widget_add_icon_element(widget, 48, 18, &I_TextTransfersDone_51x13); + widget_add_icon_element(widget, 9, 40, &I_PulsemonRightHappy_14x16); + widget_add_button_element( + widget, GuiButtonTypeRight, "OK", vb_migrate_scene_to_app_widget_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + } else { + furi_crash("Unknown new state in vb_migrate_scene_to_app_set_state"); + } + + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneToApp, state); + } +} + +static void vb_migrate_scene_to_app_load_capture(VbMigrate* inst, bool go_next) { + if(go_next) { + nfc_worker_stop(inst->worker); + vb_migrate_blink_stop(inst); + ++inst->next_id; + ++inst->num_sent; + } + + if(inst->num_sent == inst->num_captured) { + vb_migrate_scene_to_app_set_state(inst, ToAppStateComplete); + } else { + uint32_t state = scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneToApp); + inst->next_id = vb_migrate_get_next_id(inst, inst->text_store, inst->next_id, true); + FuriString* temp_str = + furi_string_alloc_printf(VB_MIGRATE_CAPTURE_FORMAT, inst->next_id, NFC_APP_EXTENSION); + + vb_migrate_show_loading_popup(inst, true); + if(vb_migrate_load_nfc(inst, inst->text_store, furi_string_get_cstr(temp_str))) { + Widget* widget = inst->widget; + + widget_reset(widget); + vb_migrate_add_bg(widget, VbMigrateBgTypeLeftButton | VbMigrateBgTypeRightButton); + widget_add_icon_element(widget, 11, 18, &I_AppInteract_32x27); + widget_add_icon_element(widget, 48, 18, &I_TextTapApp_56x27); + furi_string_printf(temp_str, "%d/%d", inst->num_sent + 1, inst->num_captured); + widget_add_string_element( + widget, + 120, + 48, + AlignRight, + AlignBottom, + FontSecondary, + furi_string_get_cstr(temp_str)); + widget_add_button_element( + widget, GuiButtonTypeLeft, "Cancel", vb_migrate_scene_to_app_widget_callback, inst); + widget_add_button_element( + widget, GuiButtonTypeRight, "Skip", vb_migrate_scene_to_app_widget_callback, inst); + + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewWidget); + + vb_migrate_scene_to_app_set_nfc_state(inst, state); + nfc_worker_start( + inst->worker, + NfcWorkerStateMfUltralightEmulate, + &inst->nfc_dev->dev_data, + vb_migrate_scene_to_app_worker_callback, + inst); + vb_migrate_blink_emulate(inst); + } else { + view_dispatcher_send_custom_event( + inst->view_dispatcher, ToAppEventTypeCaptureLoadError); + } + vb_migrate_show_loading_popup(inst, false); + furi_string_free(temp_str); + } +} + +void vb_migrate_scene_to_app_on_enter(void* context) { + VbMigrate* inst = context; + + // Perform your setup here + inst->next_id = 0; + inst->num_sent = 0; + scene_manager_set_scene_state(inst->scene_manager, VbMigrateSceneToApp, ToAppStateInitial); + vb_migrate_scene_to_app_set_state(inst, ToAppStateInstructions); +} + +bool vb_migrate_scene_to_app_on_event(void* context, SceneManagerEvent event) { + VbMigrate* inst = context; + uint32_t state = scene_manager_get_scene_state(inst->scene_manager, VbMigrateSceneToApp); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == ToAppEventTypeWidgetLeft) { + consumed = scene_manager_previous_scene(inst->scene_manager); + } else if(event.event == ToAppEventTypeWidgetRight) { + if(state == ToAppStateInstructions) { + vb_migrate_scene_to_app_set_state(inst, ToAppStateEmulateReady); + consumed = true; + } else if(state == ToAppStateEmulateReady) { + vb_migrate_scene_to_app_load_capture(inst, true); + consumed = true; + } else if(state == ToAppStateComplete) { + consumed = scene_manager_previous_scene(inst->scene_manager); + } + } else if(event.event == ToAppEventTypeEmulateStart) { + vb_migrate_scene_to_app_load_capture(inst, false); + } else if(event.event == ToAppEventTypeTagWrite) { + if(vb_migrate_scene_to_app_is_state_changed(inst, state)) { + if(state == ToAppStateEmulateReady) { + notification_message(inst->notifications, &sequence_success); + vb_migrate_scene_to_app_load_capture(inst, true); + consumed = true; + } + } + } else if(event.event == ToAppEventTypeCaptureLoadError) { + vb_migrate_scene_to_app_set_state(inst, ToAppStateLoadError); + consumed = true; + } else { + furi_crash("Unknown event in vb_migrate_scene_to_app_on_event"); + } + } + return consumed; +} + +void vb_migrate_scene_to_app_on_exit(void* context) { + VbMigrate* inst = context; + + // Perform your cleanup here + widget_reset(inst->widget); + nfc_worker_stop(inst->worker); + vb_migrate_blink_stop(inst); + notification_message_block(inst->notifications, &sequence_reset_red); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_migrate.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_migrate.c new file mode 100644 index 000000000..06846458e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_migrate.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "vb_migrate_i.h" + +#define TAG "vb_migrate" + +bool vb_migrate_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + VbMigrate* inst = context; + return scene_manager_handle_custom_event(inst->scene_manager, event); +} + +bool vb_migrate_back_event_callback(void* context) { + furi_assert(context); + VbMigrate* inst = context; + return scene_manager_handle_back_event(inst->scene_manager); +} + +void vb_migrate_blink_read(VbMigrate* inst) { + notification_message(inst->notifications, &sequence_blink_start_cyan); +} + +void vb_migrate_blink_emulate(VbMigrate* inst) { + notification_message(inst->notifications, &sequence_blink_start_magenta); +} + +void vb_migrate_blink_stop(VbMigrate* inst) { + notification_message_block(inst->notifications, &sequence_blink_stop); +} + +void vb_migrate_text_store_set(VbMigrate* inst, const char* text, ...) { + va_list args; + va_start(args, text); + + vsnprintf(inst->text_store, sizeof(inst->text_store), text, args); + + va_end(args); +} + +void vb_migrate_text_store_clear(VbMigrate* inst) { + memset(inst->text_store, 0, sizeof(inst->text_store)); +} + +bool vb_migrate_save_nfc(VbMigrate* inst, const char* dev_name, const char* file_name) { + bool saved = false; + FuriString* temp_str = furi_string_alloc(); + + do { + furi_string_printf(temp_str, "%s/%s", VB_MIGRATE_FOLDER, dev_name); + if(!storage_simply_mkdir(inst->storage, furi_string_get_cstr(temp_str))) { + dialog_message_show_storage_error(inst->dialogs, "Can not create\ndata folder"); + break; + } + furi_string_cat_printf(temp_str, "/%s", file_name); + inst->nfc_dev->format = NfcDeviceSaveFormatMifareUl; + saved = nfc_device_save(inst->nfc_dev, furi_string_get_cstr(temp_str)); + } while(false); + + furi_string_free(temp_str); + return saved; +} + +bool vb_migrate_load_nfc(VbMigrate* inst, const char* dev_name, const char* file_name) { + bool saved = false; + FuriString* temp_str = + furi_string_alloc_printf("%s/%s/%s", VB_MIGRATE_FOLDER, dev_name, file_name); + saved = nfc_device_load(inst->nfc_dev, furi_string_get_cstr(temp_str), true); + + furi_string_free(temp_str); + return saved; +} + +bool vb_migrate_delete(VbMigrate* inst, const char* dev_name, bool whole_vb) { + bool deleted = false; + FuriString* dir_path = furi_string_alloc_printf("%s/%s", VB_MIGRATE_FOLDER, dev_name); + + if(whole_vb) { + deleted = storage_simply_remove_recursive(inst->storage, furi_string_get_cstr(dir_path)); + } else { + File* dir_handle = storage_file_alloc(inst->storage); + if(storage_dir_open(dir_handle, furi_string_get_cstr(dir_path))) { + FileInfo file_info; + char name[256]; + FuriString* file_path = furi_string_alloc(); + while(storage_dir_read(dir_handle, &file_info, name, sizeof(name))) { + // Files that is .nfc, but is not template + if(!(file_info.flags & FSF_DIRECTORY) && strstr(name, NFC_APP_EXTENSION) && + !strstr(name, VB_MIGRATE_TEMPLATE_NAME)) { + furi_string_printf(file_path, "%s/%s", furi_string_get_cstr(dir_path), name); + deleted = + storage_simply_remove(inst->storage, furi_string_get_cstr(file_path)); + if(!deleted) break; + } + } + + furi_string_free(file_path); + storage_dir_close(dir_handle); + } + storage_file_free(dir_handle); + } + + furi_string_free(dir_path); + return deleted; +} + +int vb_migrate_count_captured_mons(VbMigrate* inst, const char* dev_name) { + int count = 0; + + FuriString* dir_path = furi_string_alloc_printf("%s/%s", VB_MIGRATE_FOLDER, dev_name); + File* dir_handle = storage_file_alloc(inst->storage); + if(storage_dir_open(dir_handle, furi_string_get_cstr(dir_path))) { + FileInfo file_info; + char name[256]; + while(storage_dir_read(dir_handle, &file_info, name, sizeof(name))) { + // Files that is .nfc, but is not template + if(!(file_info.flags & FSF_DIRECTORY) && strstr(name, NFC_APP_EXTENSION) && + !strstr(name, VB_MIGRATE_TEMPLATE_NAME)) + ++count; + } + + storage_dir_close(dir_handle); + } + storage_file_free(dir_handle); + furi_string_free(dir_path); + + return count; +} + +int vb_migrate_get_next_id(VbMigrate* inst, const char* dev_name, int i, bool is_load) { + FuriString* dir_path = furi_string_alloc_printf("%s/%s", VB_MIGRATE_FOLDER, dev_name); + FuriString* file_path = furi_string_alloc(); + while(true) { + furi_string_printf( + file_path, + "%s/" VB_MIGRATE_CAPTURE_FORMAT, + furi_string_get_cstr(dir_path), + i, + NFC_APP_EXTENSION); + bool exit_cond = + storage_common_stat(inst->storage, furi_string_get_cstr(file_path), NULL) == + FSE_NOT_EXIST; + if(is_load) exit_cond = !exit_cond; + if(exit_cond) break; + ++i; + } + + furi_string_free(file_path); + furi_string_free(dir_path); + return i; +} + +void vb_migrate_show_loading_popup(VbMigrate* inst, bool show) { + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_dispatcher_switch_to_view(inst->view_dispatcher, VbMigrateViewLoading); + } else { + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + +VbMigrate* vb_migrate_alloc() { + VbMigrate* inst = malloc(sizeof(VbMigrate)); + + inst->view_dispatcher = view_dispatcher_alloc(); + inst->scene_manager = scene_manager_alloc(&vb_migrate_scene_handlers, inst); + view_dispatcher_enable_queue(inst->view_dispatcher); + view_dispatcher_set_event_callback_context(inst->view_dispatcher, inst); + view_dispatcher_set_custom_event_callback( + inst->view_dispatcher, vb_migrate_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + inst->view_dispatcher, vb_migrate_back_event_callback); + + inst->clear_account_id = false; + + // GUI + inst->gui = furi_record_open(RECORD_GUI); + + // Storage + inst->storage = furi_record_open(RECORD_STORAGE); + + // Notifications service + inst->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Dialogs + inst->dialogs = furi_record_open(RECORD_DIALOGS); + + // NFC + inst->nfc_dev = nfc_device_alloc(); + inst->worker = nfc_worker_alloc(); + + // Submenu + inst->submenu = submenu_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, VbMigrateViewMenu, submenu_get_view(inst->submenu)); + + // Popup + inst->popup = popup_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, VbMigrateViewPopup, popup_get_view(inst->popup)); + + // Widget + inst->widget = widget_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, VbMigrateViewWidget, widget_get_view(inst->widget)); + + // File select + inst->file_select = file_select_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, VbMigrateViewFileSelect, file_select_get_view(inst->file_select)); + + // Text input + inst->text_input = text_input_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, VbMigrateViewTextInput, text_input_get_view(inst->text_input)); + + // Loading + inst->loading = loading_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, VbMigrateViewLoading, loading_get_view(inst->loading)); + + // Variable item list + inst->variable_list = variable_item_list_ex_alloc(); + view_dispatcher_add_view( + inst->view_dispatcher, + VbMigrateViewVariableItemList, + variable_item_list_ex_get_view(inst->variable_list)); + + // Dialog ex + // inst->dialog_ex = dialog_ex_alloc(); + // view_dispatcher_add_view( + // inst->view_dispatcher, VbMigrateViewDialogEx, dialog_ex_get_view(inst->dialog_ex)); + + return inst; +} + +void vb_migrate_free(VbMigrate* inst) { + // Dialog ex + // view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewDialogEx); + // dialog_ex_free(inst->dialog_ex); + + // Variable item list + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewVariableItemList); + variable_item_list_ex_free(inst->variable_list); + + // Loading + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewLoading); + loading_free(inst->loading); + + // Text input + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewTextInput); + text_input_free(inst->text_input); + + // File select + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewFileSelect); + file_select_free(inst->file_select); + + // Widget + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewWidget); + widget_free(inst->widget); + + // Popup + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewPopup); + popup_free(inst->popup); + + // Submenu + view_dispatcher_remove_view(inst->view_dispatcher, VbMigrateViewMenu); + submenu_free(inst->submenu); + + // NFC + nfc_worker_free(inst->worker); + nfc_device_free(inst->nfc_dev); + + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_GUI); + + view_dispatcher_free(inst->view_dispatcher); + scene_manager_free(inst->scene_manager); + + free(inst); +} + +int32_t vb_migrate_app(void* p) { + UNUSED(p); + + VbMigrate* inst = vb_migrate_alloc(); + view_dispatcher_attach_to_gui(inst->view_dispatcher, inst->gui, ViewDispatcherTypeFullscreen); + + if(storage_simply_mkdir(inst->storage, VB_MIGRATE_FOLDER)) { + scene_manager_next_scene(inst->scene_manager, VbMigrateSceneMainMenu); + view_dispatcher_run(inst->view_dispatcher); + } else { + dialog_message_show_storage_error(inst->dialogs, "Can not create\napp folder"); + } + + vb_migrate_free(inst); + return 0; +} + +void vb_migrate_add_bg(Widget* widget, VbMigrateBgType type) { + widget_add_icon_element(widget, 0, 0, &I_Background_128x64); + if(type & VbMigrateBgTypeLeftButton) { + widget_add_icon_element(widget, 0, 49, &I_LeftButtonBg_54x15); + } + if(type & VbMigrateBgTypeRightButton) { + widget_add_icon_element(widget, 74, 49, &I_RightButtonBg_54x15); + } +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_migrate.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_migrate.h new file mode 100644 index 000000000..4104cf3fe --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_migrate.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include + +#define VB_MIGRATE_VERSION "1.0" + +#define VB_MIGRATE_FOLDER ANY_PATH("vb_migrate") + +typedef struct VbMigrate VbMigrate; + +VbMigrate* vb_migrate_alloc(); + +void vb_migrate_free(VbMigrate* inst); diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_migrate_10px.png b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_migrate_10px.png new file mode 100644 index 000000000..d9e7536ff Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_migrate_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_migrate_i.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_migrate_i.h new file mode 100644 index 000000000..f4323ae9b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_migrate_i.h @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +// #include +#include "gui/modules/file_select.h" +#include +#include "gui/modules/variable_item_list_ex.h" + +#include +#include + +#include + +#include "vb_migrate.h" +#include "scenes/vb_migrate_scene.h" +#include "vb_tag.h" + +#include "vb_migrate_icons.h" + +#define VB_MIGRATE_TEMPLATE_NAME "template" NFC_APP_EXTENSION +#define VB_MIGRATE_CAPTURE_FORMAT "%03d%s" + +#define VB_MIGRATE_MAX_DEV_NAME_LENGTH (30) + +struct VbMigrate { + Gui* gui; + Storage* storage; + DialogsApp* dialogs; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + Submenu* submenu; + Popup* popup; + Widget* widget; + // DialogEx* dialog_ex; + FileSelect* file_select; + TextInput* text_input; + Loading* loading; + VariableItemListEx* variable_list; + NfcWorker* worker; + NfcDevice* nfc_dev; + char text_store[128]; + uint8_t captured_pwd[4]; + uint8_t captured_uid[7]; + bool clear_account_id; + int num_captured; + int next_id; + int num_sent; + VbTagType orig_type; + VbTagType override_type; + const VbTagProduct* orig_product; +}; + +typedef enum { + VbMigrateViewMenu, + VbMigrateViewPopup, + VbMigrateViewWidget, + VbMigrateViewTextInput, + // VbMigrateViewDialogEx, + VbMigrateViewFileSelect, + VbMigrateViewLoading, + VbMigrateViewVariableItemList, +} VbMigrateView; + +typedef enum { + VbMigrateBgTypeNone, + VbMigrateBgTypeLeftButton = 1 << 0, + VbMigrateBgTypeRightButton = 1 << 1, +} VbMigrateBgType; + +void vb_migrate_blink_read(VbMigrate* inst); +void vb_migrate_blink_emulate(VbMigrate* inst); +void vb_migrate_blink_stop(VbMigrate* inst); +void vb_migrate_text_store_set(VbMigrate* inst, const char* text, ...); +void vb_migrate_text_store_clear(VbMigrate* inst); +bool vb_migrate_save_nfc(VbMigrate* inst, const char* dev_name, const char* file_name); +bool vb_migrate_load_nfc(VbMigrate* inst, const char* dev_name, const char* file_name); +int vb_migrate_count_captured_mons(VbMigrate* inst, const char* dev_name); +bool vb_migrate_delete(VbMigrate* inst, const char* dev_name, bool whole_vb); +int vb_migrate_get_next_id(VbMigrate* inst, const char* dev_name, int i, bool is_load); +void vb_migrate_show_loading_popup(VbMigrate* inst, bool show); +void vb_migrate_add_bg(Widget* widget, VbMigrateBgType type); diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_tag.c b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_tag.c new file mode 100644 index 000000000..3020e67a5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_tag.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include "vb_tag.h" + +#define VB_NAME_VBDM "VB Digital Monster" +#define VB_NAME_VBV "VB Digivice -V-" +#define VB_NAME_VBC "VB Characters" +#define VB_NAME_VH "Vital Hero" +#define VB_NAME_VBBE "Vital Bracelet BE" + +#define VB_NAME_VBDM_SHORT "VBDM" +#define VB_NAME_VBV_SHORT "VBV" +#define VB_NAME_VBC_SHORT "VBC" +#define VB_NAME_VH_SHORT "VH" +#define VB_NAME_VBBE_SHORT "VBBE" + +struct BantBlockCommon { + uint32_t magic; + // Note: this should be big endian, but for convenience, we'll treat them as little endian + uint16_t item_id; + uint16_t item_no; + uint8_t status; +} __attribute__((packed)); + +struct BantBlockVb { + uint8_t dim_no; + uint8_t operation; + uint8_t reserved; + uint8_t app_flag; + uint8_t padding[3]; +} __attribute__((packed)); + +struct BantBlockBe { + uint8_t operation; + uint16_t dim_no; + uint8_t app_flag; + uint8_t nonce[3]; +} __attribute__((packed)); + +struct BantBlock { + struct BantBlockCommon common; + union { + struct BantBlockVb vb; + struct BantBlockBe be; + }; +} __attribute__((packed)); + +static const VbTagProduct vb_tag_valid_products[] = { + {.item_id = 0x0200, + .item_no = 0x0100, + .name = VB_NAME_VBDM, + .short_name = VB_NAME_VBDM_SHORT, + .type = VbTagTypeVBDM}, + {.item_id = 0x0200, + .item_no = 0x0200, + .name = VB_NAME_VBDM, + .short_name = VB_NAME_VBDM_SHORT, + .type = VbTagTypeVBDM}, + {.item_id = 0x0200, + .item_no = 0x0300, + .name = VB_NAME_VBDM, + .short_name = VB_NAME_VBDM_SHORT, + .type = VbTagTypeVBDM}, + {.item_id = 0x0200, + .item_no = 0x0400, + .name = VB_NAME_VBV, + .short_name = VB_NAME_VBV_SHORT, + .type = VbTagTypeVBV}, + {.item_id = 0x0200, + .item_no = 0x0500, + .name = VB_NAME_VBV, + .short_name = VB_NAME_VBV_SHORT, + .type = VbTagTypeVBV}, + {.item_id = 0x0200, + .item_no = 0x0600, + .name = VB_NAME_VH, + .short_name = VB_NAME_VH_SHORT, + .type = VbTagTypeVH}, + {.item_id = 0x0300, + .item_no = 0x0100, + .name = VB_NAME_VBC, + .short_name = VB_NAME_VBC_SHORT, + .type = VbTagTypeVBC}, + {.item_id = 0x0400, + .item_no = 0x0100, + .name = VB_NAME_VBBE, + .short_name = VB_NAME_VBBE_SHORT, + .type = VbTagTypeVBBE}, +}; + +static const char* vb_tag_type_names[] = { + "Unknown", + VB_NAME_VBDM_SHORT, + VB_NAME_VBV_SHORT, + VB_NAME_VBC_SHORT, + VB_NAME_VH_SHORT, + VB_NAME_VBBE_SHORT, +}; + +BantBlock* vb_tag_get_bant_block(NfcDeviceData* dev) { + return (BantBlock*)&dev->mf_ul_data.data[16]; +} + +const VbTagProduct* vb_tag_find_product(const BantBlock* bant) { + for(size_t i = 0; i < COUNT_OF(vb_tag_valid_products); ++i) { + const VbTagProduct* product = &vb_tag_valid_products[i]; + if(bant->common.item_id == product->item_id && bant->common.item_no == product->item_no) + return product; + } + + return NULL; +} + +bool vb_tag_validate_product(NfcDeviceData* dev) { + // Must be NTAG I2C Plus 1k + if(dev->protocol != NfcDeviceProtocolMifareUl) return false; + if(dev->mf_ul_data.type != MfUltralightTypeNTAGI2CPlus1K) return false; + // Must match one of the known product IDs + BantBlock* bant = vb_tag_get_bant_block(dev); + if(bant->common.magic != BANT_MAGIC) return false; + return vb_tag_find_product(bant) != NULL; +} + +VbTagStatus vb_tag_get_status(const BantBlock* bant) { + return bant->common.status; +} + +void vb_tag_set_status(BantBlock* bant, VbTagStatus status) { + bant->common.status = status; +} + +VbTagOperation vb_tag_get_operation(const BantBlock* bant) { + return vb_tag_is_vbbe(bant) ? bant->be.operation : bant->vb.operation; +} + +void vb_tag_set_operation(BantBlock* bant, VbTagOperation operation) { + uint8_t* p_op = vb_tag_is_vbbe(bant) ? &bant->be.operation : &bant->vb.operation; + *p_op = operation; +} + +const VbTagProduct* vb_tag_get_default_product(VbTagType type) { + // IMPORTANT: Update when vb_tag_valid_products changes + switch(type) { + case VbTagTypeVBDM: + return &vb_tag_valid_products[2]; + case VbTagTypeVBV: + return &vb_tag_valid_products[4]; + case VbTagTypeVBC: + return &vb_tag_valid_products[6]; + case VbTagTypeVH: + return &vb_tag_valid_products[5]; + case VbTagTypeVBBE: + return &vb_tag_valid_products[7]; + + default: + return NULL; + } +} + +void vb_tag_set_item_id_no(BantBlock* bant, const VbTagProduct* product) { + bant->common.item_id = product->item_id; + bant->common.item_no = product->item_no; +} + +const char* vb_tag_get_tag_type_name(VbTagType type) { + if(type < VbTagTypeMax) { + return vb_tag_type_names[type]; + } else { + return NULL; + } +} + +bool vb_tag_get_app_flag(const BantBlock* bant) { + uint8_t app_flag = vb_tag_is_vbbe(bant) ? bant->be.app_flag : bant->vb.app_flag; + return app_flag == 1; +} + +void vb_tag_set_app_flag(BantBlock* bant, bool value) { + uint8_t* app_flag = vb_tag_is_vbbe(bant) ? &bant->be.app_flag : &bant->vb.app_flag; + *app_flag = value ? 1 : 0xff; +} + +// Lookup is expensive, let's check tag ID directly +bool vb_tag_is_vbbe(const BantBlock* bant) { + return bant->common.item_id == 0x0400; +} + +uint32_t vb_tag_get_nonce(const BantBlock* bant) { + if(vb_tag_is_vbbe(bant)) { + return (bant->be.nonce[0] << 16) | (bant->be.nonce[1] << 8) | bant->be.nonce[2]; + } else { + return 0; + } +} + +void vb_tag_set_nonce(BantBlock* bant, uint32_t value) { + if(vb_tag_is_vbbe(bant)) { + uint8_t* nonce = bant->be.nonce; + nonce[0] = value >> 16; + nonce[1] = value >> 8; + nonce[2] = value; + } +} + +void vb_tag_set_random_nonce(BantBlock* bant) { + if(vb_tag_is_vbbe(bant)) { + uint32_t orig_nonce = vb_tag_get_nonce(bant); + uint32_t new_nonce; + do { + new_nonce = rand() & 0xffffff; + // Original nonce is generated as ((rand() % 0xffff) << 8) | (rand() % 0xff), + // so don't inclue 0xffff** and 0x****ff as valid values + if((new_nonce & 0xff) == 0xff) --new_nonce; + if(new_nonce >= 0xffff00) new_nonce -= 0x100; + } while(new_nonce == 0 || new_nonce == orig_nonce); + vb_tag_set_nonce(bant, new_nonce); + } +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_tag.h b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_tag.h new file mode 100644 index 000000000..ee1375a8f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/flipperzero_vb_migrate/vb_tag.h @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// +// VB Lab Migration Assistant for Flipper Zero +// Copyright (C) 2022 cyanic +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include +#include + +#define BANT_MAGIC (0x544E4142) + +typedef struct BantBlock BantBlock; + +typedef enum { + VbTagTypeUnknown, + VbTagTypeVBDM, + VbTagTypeVBV, + VbTagTypeVBC, + VbTagTypeVH, + VbTagTypeVBBE, + VbTagTypeMax +} VbTagType; + +typedef struct { + uint16_t item_id; + uint16_t item_no; + const char* name; + const char* short_name; + VbTagType type; +} VbTagProduct; + +typedef enum { + VbTagStatusReady = 1 << 0, + VbTagStatusDimReady = 1 << 1, + VbTagStatusActiveIsAvatar = 1 << 2, +} VbTagStatus; + +typedef enum { + VbTagOperationIdle, + VbTagOperationReady, + VbTagOperationTransferToApp, + VbTagOperationCheckDim, + VbTagOperationReturnFromApp, + VbTagOperationSpotInit, + VbTagOperationSpotCommit, +} VbTagOperation; + +BantBlock* vb_tag_get_bant_block(NfcDeviceData* dev); +const VbTagProduct* vb_tag_find_product(const BantBlock* bant); +bool vb_tag_validate_product(NfcDeviceData* dev); +VbTagStatus vb_tag_get_status(const BantBlock* bant); +void vb_tag_set_status(BantBlock* bant, VbTagStatus status); +VbTagOperation vb_tag_get_operation(const BantBlock* bant); +void vb_tag_set_operation(BantBlock* bant, VbTagOperation operation); +const VbTagProduct* vb_tag_get_default_product(VbTagType type); +void vb_tag_set_item_id_no(BantBlock* bant, const VbTagProduct* product); +const char* vb_tag_get_tag_type_name(VbTagType type); +bool vb_tag_get_app_flag(const BantBlock* bant); +void vb_tag_set_app_flag(BantBlock* bant, bool value); +bool vb_tag_is_vbbe(const BantBlock* bant); +uint32_t vb_tag_get_nonce(const BantBlock* bant); +void vb_tag_set_nonce(BantBlock* bant, uint32_t value); +void vb_tag_set_random_nonce(BantBlock* bant); diff --git a/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/README.md b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/README.md new file mode 100644 index 000000000..3cd3d4d53 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/README.md @@ -0,0 +1,11 @@ +## Simple count down timer application for flipper zero + +### How to use +`up/down`: set second/minute/hour value. + +`ok`: start/stop counting. + +`long press on ok`: stop counting and reset counter. + +`left/right`: select second/minute/hour value. + diff --git a/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/app.c b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/app.c new file mode 100644 index 000000000..5c03ea246 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/app.c @@ -0,0 +1,71 @@ +#include "views/countdown_view.h" +#include "app.h" + +static void register_view(ViewDispatcher* dispatcher, View* view, uint32_t viewid); + +int32_t app_main(void* p) { + UNUSED(p); + + CountDownTimerApp* app = countdown_app_new(); + + countdown_app_run(app); + + countdown_app_delete(app); + + return 0; +} + +static uint32_t view_exit(void* ctx) { + furi_assert(ctx); + + return VIEW_NONE; +} + +CountDownTimerApp* countdown_app_new(void) { + CountDownTimerApp* app = (CountDownTimerApp*)(malloc(sizeof(CountDownTimerApp))); + + // 1.1 open gui + app->gui = furi_record_open(RECORD_GUI); + + // 2.1 setup view dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + // 2.2 attach view dispatcher to gui + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // 2.3 attach views to the dispatcher + // helloworld view + app->helloworld_view = countdown_timer_view_new(); + register_view(app->view_dispatcher, countdown_timer_view_get_view(app->helloworld_view), 0xff); + + // 2.5 switch to default view + view_dispatcher_switch_to_view(app->view_dispatcher, 0xff); + + return app; +} + +void countdown_app_delete(CountDownTimerApp* app) { + furi_assert(app); + + // delete views + view_dispatcher_remove_view(app->view_dispatcher, 0xff); + countdown_timer_view_delete(app->helloworld_view); // hello world view + + // delete view dispatcher + view_dispatcher_free(app->view_dispatcher); + furi_record_close(RECORD_GUI); + + // self + free(app); +} + +void countdown_app_run(CountDownTimerApp* app) { + view_dispatcher_run(app->view_dispatcher); +} + +static void register_view(ViewDispatcher* dispatcher, View* view, uint32_t viewid) { + view_dispatcher_add_view(dispatcher, viewid, view); + + view_set_previous_callback(view, view_exit); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/app.h b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/app.h new file mode 100644 index 000000000..413b3dbbf --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/app.h @@ -0,0 +1,22 @@ +#ifndef __APP_H__ +#define __APP_H__ + +#include +#include +#include + +// app +typedef struct { + Gui* gui; // gui object + ViewDispatcher* view_dispatcher; // view dispacther of the gui + + // views + CountDownTimView* helloworld_view; + +} CountDownTimerApp; + +CountDownTimerApp* countdown_app_new(void); +void countdown_app_delete(CountDownTimerApp* app); +void countdown_app_run(CountDownTimerApp* app); + +#endif \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/application.fam b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/application.fam new file mode 100644 index 000000000..419b63125 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/application.fam @@ -0,0 +1,16 @@ +# qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md + +App( + appid="cntdown_tim", + name="Count Down Timer", + apptype=FlipperAppType.EXTERNAL, + entry_point="app_main", + cdefines=["APP_COUNT_DOWN_TIMER"], + requires=[ + "gui", + ], + stack_size=2 * 1024, + order=20, + fap_icon="cntdown_timer.png", + fap_category="Misc_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/cntdown_timer.png b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/cntdown_timer.png new file mode 100644 index 000000000..b25c2718e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/cntdown_timer.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/utils/utils.c b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/utils/utils.c new file mode 100644 index 000000000..8ce82f1c6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/utils/utils.c @@ -0,0 +1,34 @@ +#include +#include "utils.h" + +static const NotificationSequence sequence_beep = { + &message_blue_255, + &message_note_d5, + &message_delay_100, + &message_sound_off, + + NULL, +}; + +void notification_beep_once() { + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_beep); + notification_off(); +} + +void notification_off() { + furi_record_close(RECORD_NOTIFICATION); +} + +void notification_timeup() { + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_audiovisual_alert); +} + +void parse_sec_to_time_str(char* buffer, size_t len, int32_t sec) { + snprintf( + buffer, + len, + "%02ld:%02ld:%02ld", + (sec % (60 * 60 * 24)) / (60 * 60), // hour + (sec % (60 * 60)) / 60, // minute + sec % 60); // second +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/utils/utils.h b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/utils/utils.h new file mode 100644 index 000000000..c72db7319 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/utils/utils.h @@ -0,0 +1,12 @@ +#ifndef __UTILS_H__ +#define __UTILS_H__ +#include +#include + +void notification_beep_once(); +void notification_off(); +void notification_timeup(); + +void parse_sec_to_time_str(char *buffer, size_t len, int32_t sec); + +#endif // __UTILS_H__ \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/views/countdown_view.c b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/views/countdown_view.c new file mode 100644 index 000000000..97e8cb248 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/views/countdown_view.c @@ -0,0 +1,346 @@ +#include "countdown_view.h" +#include "../utils/utils.h" + +// internal +static void handle_misc_cmd(CountDownTimView* hw, CountDownViewCmd cmd); +static void handle_time_setting_updown(CountDownTimView* cdv, CountDownViewCmd cmd); +static void handle_time_setting_select(InputKey key, CountDownTimView* cdv); +static void draw_selection(Canvas* canvas, CountDownViewSelect selection); + +static void countdown_timer_start_counting(CountDownTimView* cdv); +static void countdown_timer_pause_counting(CountDownTimView* cdv); + +// callbacks +static void countdown_timer_view_on_enter(void* ctx); +static void countdown_timer_view_on_draw(Canvas* canvas, void* ctx); +static bool countdown_timer_view_on_input(InputEvent* event, void* ctx); +static void timer_cb(void* ctx); + +CountDownTimView* countdown_timer_view_new() { + CountDownTimView* cdv = (CountDownTimView*)(malloc(sizeof(CountDownTimView))); + + cdv->view = view_alloc(); + + cdv->timer = furi_timer_alloc(timer_cb, FuriTimerTypePeriodic, cdv); + + cdv->counting = false; + + view_set_context(cdv->view, cdv); + + view_allocate_model(cdv->view, ViewModelTypeLocking, sizeof(CountDownModel)); + + view_set_draw_callback(cdv->view, countdown_timer_view_on_draw); + view_set_input_callback(cdv->view, countdown_timer_view_on_input); + view_set_enter_callback(cdv->view, countdown_timer_view_on_enter); + + return cdv; +} + +void countdown_timer_view_delete(CountDownTimView* cdv) { + furi_assert(cdv); + + view_free(cdv->view); + furi_timer_stop(cdv->timer); + furi_timer_free(cdv->timer); + + free(cdv); +} + +View* countdown_timer_view_get_view(CountDownTimView* cdv) { + return cdv->view; +} + +void countdown_timer_view_state_reset(CountDownTimView* cdv) { + cdv->counting = false; + + with_view_model( + cdv->view, CountDownModel * model, { model->count = model->saved_count_setting; }, true) +} + +void countdown_timer_state_toggle(CountDownTimView* cdv) { + bool on = cdv->counting; + if(!on) { + countdown_timer_start_counting(cdv); + } else { + countdown_timer_pause_counting(cdv); + } + + cdv->counting = !on; +} + +// on enter callback, CountDownTimView as ctx +static void countdown_timer_view_on_enter(void* ctx) { + furi_assert(ctx); + + CountDownTimView* cdv = (CountDownTimView*)ctx; + + // set current count to a initial value + with_view_model( + cdv->view, + CountDownModel * model, + { + model->count = INIT_COUNT; + model->saved_count_setting = INIT_COUNT; + }, + true); +} + +// view draw callback, CountDownModel as ctx +static void countdown_timer_view_on_draw(Canvas* canvas, void* ctx) { + furi_assert(ctx); + CountDownModel* model = (CountDownModel*)ctx; + + char buffer[64]; + + int32_t count = model->count; + int32_t expected_count = model->saved_count_setting; + + CountDownViewSelect select = model->select; + + // elements_frame(canvas, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + canvas_set_font(canvas, FontBigNumbers); + draw_selection(canvas, select); + + parse_sec_to_time_str(buffer, sizeof(buffer), count); + canvas_draw_str_aligned( + canvas, SCREEN_CENTER_X, SCREEN_CENTER_Y, AlignCenter, AlignCenter, buffer); + + elements_progress_bar(canvas, 0, 0, SCREEN_WIDTH, (1.0 * count / expected_count)); +} + +// keys input event callback, CountDownTimView as ctx +static bool countdown_timer_view_on_input(InputEvent* event, void* ctx) { + furi_assert(ctx); + + CountDownTimView* hw = (CountDownTimView*)ctx; + + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + switch(event->key) { + case InputKeyUp: + case InputKeyDown: + case InputKeyRight: + case InputKeyLeft: + handle_time_setting_select(event->key, hw); + break; + + case InputKeyOk: + if(event->type == InputTypeShort) { + handle_misc_cmd(hw, CountDownTimerToggleCounting); + } + break; + + default: + break; + } + + return true; + } + + if(event->type == InputTypeLong) { + switch(event->key) { + case InputKeyOk: + handle_misc_cmd(hw, CountDownTimerReset); + break; + + case InputKeyBack: + return false; + break; + + default: + break; + } + + return true; + } + + return false; +} + +static void timer_cb(void* ctx) { + furi_assert(ctx); + + CountDownTimView* cdv = (CountDownTimView*)ctx; + + int32_t count; + bool timeup = false; + + // decrement counter + with_view_model( + cdv->view, + CountDownModel * model, + { + count = model->count; + count--; + + // check timeup + if(count <= 0) { + count = 0; + timeup = true; + } + + model->count = count; + }, + true); + + if(timeup) { + handle_misc_cmd(cdv, CountDownTimerTimeUp); + } +} + +static void handle_time_setting_updown(CountDownTimView* cdv, CountDownViewCmd cmd) { + int32_t count; + + with_view_model( + cdv->view, + CountDownModel * model, + { + count = model->count; + switch(cmd) { + case CountDownTimerMinuteUp: + count += 60; + break; + case CountDownTimerMinuteDown: + count -= 60; + break; + case CountDownTimerHourDown: + count -= 3600; + break; + case CountDownTimerHourUp: + count += 3600; + break; + case CountDownTimerSecUp: + count++; + break; + case CountDownTimerSecDown: + count--; + break; + default: + break; + } + + if(count < 0) { + count = 0; + } + + // update count state + model->count = count; + + // save the count time setting + model->saved_count_setting = count; + }, + true); +} + +static void handle_misc_cmd(CountDownTimView* hw, CountDownViewCmd cmd) { + switch(cmd) { + case CountDownTimerTimeUp: + notification_timeup(); + break; + + case CountDownTimerReset: + furi_timer_stop(hw->timer); + countdown_timer_view_state_reset(hw); + notification_off(); + + break; + + case CountDownTimerToggleCounting: + countdown_timer_state_toggle(hw); + break; + + default: + break; + } + + return; +} + +static void handle_time_setting_select(InputKey key, CountDownTimView* cdv) { + bool counting = cdv->counting; + CountDownViewCmd setting_cmd = CountDownTimerSecUp; + CountDownViewSelect selection; + + if(counting) { + return; + } + + // load current selection from model context + with_view_model( + cdv->view, CountDownModel * model, { selection = model->select; }, false); + + // select + switch(key) { + case InputKeyUp: + switch(selection) { + case CountDownTimerSelectSec: + setting_cmd = CountDownTimerSecUp; + break; + case CountDownTimerSelectMinute: + setting_cmd = CountDownTimerMinuteUp; + break; + case CountDownTimerSelectHour: + setting_cmd = CountDownTimerHourUp; + break; + } + + handle_time_setting_updown(cdv, setting_cmd); + break; + + case InputKeyDown: + switch(selection) { + case CountDownTimerSelectSec: + setting_cmd = CountDownTimerSecDown; + break; + case CountDownTimerSelectMinute: + setting_cmd = CountDownTimerMinuteDown; + break; + case CountDownTimerSelectHour: + setting_cmd = CountDownTimerHourDown; + break; + } + + handle_time_setting_updown(cdv, setting_cmd); + break; + + case InputKeyRight: + selection--; + selection = selection % 3; + break; + + case InputKeyLeft: + selection++; + selection = selection % 3; + break; + + default: + break; + } + + // save selection to model context + with_view_model( + cdv->view, CountDownModel * model, { model->select = selection; }, false); +} + +static void draw_selection(Canvas* canvas, CountDownViewSelect selection) { + switch(selection) { + case CountDownTimerSelectSec: + elements_slightly_rounded_box(canvas, SCREEN_CENTER_X + 25, SCREEN_CENTER_Y + 11, 24, 2); + break; + case CountDownTimerSelectMinute: + elements_slightly_rounded_box(canvas, SCREEN_CENTER_X - 10, SCREEN_CENTER_Y + 11, 21, 2); + break; + case CountDownTimerSelectHour: + elements_slightly_rounded_box(canvas, SCREEN_CENTER_X - 47, SCREEN_CENTER_Y + 11, 24, 2); + break; + } +} + +static void countdown_timer_start_counting(CountDownTimView* cdv) { + furi_timer_start(cdv->timer, furi_kernel_get_tick_frequency() * 1); // 1s +} + +static void countdown_timer_pause_counting(CountDownTimView* cdv) { + furi_timer_stop(cdv->timer); + notification_off(); +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/views/countdown_view.h b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/views/countdown_view.h new file mode 100644 index 000000000..ed8114f8e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/fpz_cntdown_timer-main/views/countdown_view.h @@ -0,0 +1,59 @@ +#ifndef __COUNTDOWN_VIEW_H__ +#define __COUNTDOWN_VIEW_H__ + +#include +#include +#include +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define SCREEN_CENTER_X (SCREEN_WIDTH / 2) +#define SCREEN_CENTER_Y (SCREEN_HEIGHT / 2) + +#define INIT_COUNT 10 + +typedef enum { + CountDownTimerMinuteUp, + CountDownTimerMinuteDown, + CountDownTimerSecDown, + CountDownTimerSecUp, + CountDownTimerHourUp, + CountDownTimerHourDown, + CountDownTimerReset, + CountDownTimerTimeUp, + CountDownTimerToggleCounting, +} CountDownViewCmd; + +typedef enum { + CountDownTimerSelectSec, + CountDownTimerSelectMinute, + CountDownTimerSelectHour, +} CountDownViewSelect; + +typedef struct { + int32_t count; + int32_t saved_count_setting; + CountDownViewSelect select; // setting +} CountDownModel; + +typedef struct { + View* view; + FuriTimer* timer; // 1Hz tick timer + bool counting; + +} CountDownTimView; + +// functions +// allocate helloworld view +CountDownTimView* countdown_timer_view_new(); + +// delete helloworld view +void countdown_timer_view_delete(CountDownTimView* cdv); + +// return view +View* countdown_timer_view_get_view(CountDownTimView* cdv); + +void countdown_timer_view_state_reset(CountDownTimView* cdv); // set initial state +void countdown_timer_state_toggle(CountDownTimView* cdv); +#endif // __COUNTDOWN_VIEW_H__ \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/game2048/2048.png b/Applications/Official/DEV_FW/source/xMasterX/game2048/2048.png new file mode 100644 index 000000000..6f46d4de5 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/game2048/2048.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/game2048/application.fam b/Applications/Official/DEV_FW/source/xMasterX/game2048/application.fam new file mode 100644 index 000000000..efafb9b82 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/game2048/application.fam @@ -0,0 +1,12 @@ +App( + appid="2048", + name="2048", + apptype=FlipperAppType.EXTERNAL, + entry_point="game_2048_app", + cdefines=["APP_2048_GAME"], + requires=["gui"], + stack_size=2 * 1024, + order=10, + fap_icon="2048.png", + fap_category="Games_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/game2048/font.c b/Applications/Official/DEV_FW/source/xMasterX/game2048/font.c new file mode 100644 index 000000000..9acfe8d23 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/game2048/font.c @@ -0,0 +1,155 @@ +#include +#include +#include + +/* 7px 3 width digit font by Sefjor + * digit encoding example + *7 111 + *6 101 + *5 101 + *4 101 + *3 101 + *2 101 + *1 111 + *0 000 this string is empty, used to align + * ? ? ? + * FE 82 FE //0 + */ + +static uint8_t font[10][3] = { + {0xFE, 0x82, 0xFE}, // 0; + {0x00, 0xFE, 0x00}, // 1; + {0xF2, 0x92, 0x9E}, // 2; + {0x92, 0x92, 0xFE}, // 3; + {0x1E, 0x10, 0xFE}, // 4; + {0x9E, 0x92, 0xF2}, // 5; + {0xFE, 0x92, 0xF2}, // 6; + {0x02, 0x02, 0xFE}, // 7; + {0xFE, 0x92, 0xFE}, // 8; + {0x9E, 0x92, 0xFE}, // 9; +}; + +#define FONT_HEIGHT 8 +#define FONT_WIDTH 3 + +static void game_2048_draw_black_point(Canvas* const canvas, uint8_t x, uint8_t y) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_dot(canvas, x, y); +} + +static void game_2048_draw_white_square(Canvas* const canvas, uint8_t x, uint8_t y) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, x, y, 15 - 1, 15 - 3); +} + +static void _game_2048_draw_column( + Canvas* const canvas, + int digit, + int coord_x, + int coord_y, + uint8_t column) { + for(int x = 0; x < FONT_HEIGHT; ++x) { + bool is_filled = (font[digit][column] >> x) & 0x1; + if(is_filled) { + game_2048_draw_black_point(canvas, coord_x, coord_y + x); + } + } +} + +static uint8_t + _game_2048_draw_digit(Canvas* const canvas, uint8_t digit, uint8_t coord_x, uint8_t coord_y) { + uint8_t x_shift = 0; + + if(digit != 1) { + for(int column = 0; column < FONT_WIDTH; column++) { + _game_2048_draw_column(canvas, digit, coord_x + column, coord_y, column); + } + x_shift = 3; + } else { + _game_2048_draw_column(canvas, digit, coord_x, coord_y, true); + x_shift = 1; + } + + return x_shift; +} + +/* We drawing text field with 1px white border + * at given coords. Total size is: + * x = 9 = 1 + 7 + 1 + * y = 1 + total text width + 1 + */ + +/* + * Returns array of digits and it's size, + * digits should be at least 4 size + * works from 1 to 9999 + */ +static void _game_2048_parse_number(uint16_t number, uint8_t* digits, uint8_t* size) { + *size = 0; + uint16_t divider = 1000; + //find first digit, result is highest divider + while(number / divider == 0) { + divider /= 10; + if(divider == 0) { + break; + } + } + + for(int i = 0; divider != 0; i++) { + digits[i] = number / divider; + number %= divider; + *size += 1; + divider /= 10; + } +} + +uint8_t _game_2048_calculate_shift(uint16_t num) { + uint8_t shift = 0; + switch(num) { + case 1: + shift = 7; + break; + case 2: + case 4: + case 8: + shift = 6; + break; + case 16: + shift = 5; + break; + case 32: + case 64: + shift = 4; + break; + case 128: + shift = 3; + break; + case 256: + shift = 2; + break; + case 512: + shift = 3; + break; + case 1024: + shift = 2; + break; + } + return shift; +} + +void game_2048_draw_number(Canvas* const canvas, uint8_t x, uint8_t y, int number) { + uint8_t digits[4]; + uint8_t size; + + _game_2048_parse_number(number, digits, &size); + if(number > 512) { + game_2048_draw_white_square(canvas, x, y); + } + + x += _game_2048_calculate_shift(number); + y += 4; + for(int i = 0; i < size; ++i) { + x += _game_2048_draw_digit(canvas, digits[i], x, y); + x++; + } +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/game2048/font.h b/Applications/Official/DEV_FW/source/xMasterX/game2048/font.h new file mode 100644 index 000000000..500123ac3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/game2048/font.h @@ -0,0 +1,3 @@ +#include + +void game_2048_draw_number(Canvas* const canvas, uint8_t x, uint8_t y, int number); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/game2048/game_2048.c b/Applications/Official/DEV_FW/source/xMasterX/game2048/game_2048.c new file mode 100644 index 000000000..77b85e8da --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/game2048/game_2048.c @@ -0,0 +1,494 @@ +#include +#include +#include +#include + +#include "font.h" + +#define DEBUG false +/* + 0 empty + 1 2 + 2 4 + 3 8 + 4 16 + 5 32 + 6 64 + 7 128 + 8 256 + 9 512 +10 1024 +11 2048 +12 4096 +... + */ +typedef uint8_t cell_state; + +/* DirectionLeft <-- +┌╌╌╌╌┐┌╌╌╌╌┐┌╌╌╌╌┐┌╌╌╌╌┐ +╎ ╎╎ ╎╎ ╎╎ ╎ +└╌╌╌╌┘└╌╌╌╌┘└╌╌╌╌┘└╌╌╌╌┘ +┌╌╌╌╌┐┌╌╌╌╌┐┌╌╌╌╌┐┌╌╌╌╌┐ +╎ ╎╎ ╎╎ ╎╎ ╎ +└╌╌╌╌┘└╌╌╌╌┘└╌╌╌╌┘└╌╌╌╌┘ +┌╌╌┌╌╌╌╌┐╌╌┐┌╌╌╌╌┐┌╌╌╌╌┐ +╎ 2╎ 2 ╎ ╎╎ ╎╎ ╎ +└╌╌└╌╌╌╌┘╌╌┘└╌╌╌╌┘└╌╌╌╌┘ +┌╌╌┌╌╌╌╌┐┌╌╌┌╌╌╌╌┐┌╌╌╌╌┐ +╎ 4╎ 4 ╎╎ 2╎ 2 ╎╎ ╎ +└╌╌└╌╌╌╌┘└╌╌└╌╌╌╌┘└╌╌╌╌┘ +*/ +typedef enum { + DirectionIdle, + DirectionUp, + DirectionRight, + DirectionDown, + DirectionLeft, +} Direction; + +typedef struct { + uint8_t y; // 0 <= y <= 3 + uint8_t x; // 0 <= x <= 3 +} Point; + +typedef struct { + uint32_t gameScore; + uint32_t highScore; +} Score; + +typedef struct { + /* + +----X + | + | field[x][y] + Y + */ + uint8_t field[4][4]; + + uint8_t next_field[4][4]; + + Score score; // original scoring + + Direction direction; + /* + field { + animation-timing-function: linear; + animation-duration: 300ms; + } + */ + uint32_t animation_start_ticks; + + Point keyframe_from[4][4]; + + Point keyframe_to[4][4]; + + bool debug; + +} GameState; + +#define XtoPx(x) (33 + x * 15) + +#define YtoPx(x) (1 + y * 15) + +static void game_2048_render_callback(Canvas* const canvas, ValueMutex* const vm) { + const GameState* game_state = acquire_mutex(vm, 25); + if(game_state == NULL) { + return; + } + + // Before the function is called, the state is set with the canvas_reset(canvas) + + if(game_state->direction == DirectionIdle) { + for(uint8_t y = 0; y < 4; y++) { + for(uint8_t x = 0; x < 4; x++) { + uint8_t field = game_state->field[y][x]; + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, XtoPx(x), YtoPx(y), 16, 16); + if(field != 0) { + game_2048_draw_number(canvas, XtoPx(x), YtoPx(y), 1 << field); + } + } + } + + // display score + char buffer[12]; + snprintf(buffer, sizeof(buffer), "%lu", game_state->score.gameScore); + canvas_draw_str_aligned(canvas, 127, 8, AlignRight, AlignBottom, buffer); + + if(game_state->score.highScore > 0) { + char buffer2[12]; + snprintf(buffer2, sizeof(buffer2), "%lu", game_state->score.highScore); + canvas_draw_str_aligned(canvas, 127, 62, AlignRight, AlignBottom, buffer2); + } + } else { // if animation + // for animation + // (osKernelGetSysTimerCount() - game_state->animation_start_ticks) / osKernelGetSysTimerFreq(); + + // TODO: end animation event/callback/set AnimationIdle + } + + release_mutex(vm, game_state); +} + +static void + game_2048_input_callback(const InputEvent* const input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +// if return false then Game Over +static bool game_2048_set_new_number(GameState* const game_state) { + uint8_t empty = 0; + for(uint8_t y = 0; y < 4; y++) { + for(uint8_t x = 0; x < 4; x++) { + if(game_state->field[y][x] == 0) { + empty++; + } + } + } + + if(empty == 0) { + return false; + } + + if(empty == 1) { + // If it is 1 move before losing, we help the player and get rid of randomness. + for(uint8_t y = 0; y < 4; y++) { + for(uint8_t x = 0; x < 4; x++) { + if(game_state->field[y][x] == 0) { + bool haveFour = + // +----X + // | + // | field[x][y], 0 <= x, y <= 3 + // Y + + // up == 4 or + (y > 0 && game_state->field[y - 1][x] == 2) || + // right == 4 or + (x < 3 && game_state->field[y][x + 1] == 2) || + // down == 4 + (y < 3 && game_state->field[y + 1][x] == 2) || + // left == 4 + (x > 0 && game_state->field[y][x - 1] == 2); + + if(haveFour) { + game_state->field[y][x] = 2; + return true; + } + + game_state->field[y][x] = 1; + return true; + } + } + } + } + + uint8_t target = rand() % empty; + uint8_t twoOrFore = rand() % 4 < 3; + for(uint8_t y = 0; y < 4; y++) { + for(uint8_t x = 0; x < 4; x++) { + if(game_state->field[y][x] == 0) { + if(target == 0) { + if(twoOrFore) { + game_state->field[y][x] = 1; // 2^1 == 2 75% + } else { + game_state->field[y][x] = 2; // 2^2 == 4 25% + } + goto exit; + } + target--; + } + } + } +exit: + return true; +} + +// static void game_2048_process_row(uint8_t before[4], uint8_t *(after[4])) { +// // move 1 row left. +// for(uint8_t i = 0; i <= 2; i++) { +// if(before[i] != 0 && before[i] == before[i + 1]) { +// before[i]++; +// before[i + 1] = 0; +// i++; +// } +// } +// for(uint8_t i = 0, j = 0; i <= 3; i++) { +// if (before[i] != 0) { +// before[j] = before[i]; +// i++; +// } +// } +// } + +static void game_2048_process_move(GameState* const game_state) { + memset(game_state->next_field, 0, sizeof(game_state->next_field)); + // +----X + // | + // | field[x][y], 0 <= x, y <= 3 + // Y + + // up + if(game_state->direction == DirectionUp) { + for(uint8_t x = 0; x < 4; x++) { + uint8_t next_y = 0; + for(int8_t y = 0; y < 4; y++) { + uint8_t field = game_state->field[y][x]; + if(field == 0) { + continue; + } + + if(game_state->next_field[next_y][x] == 0) { + game_state->next_field[next_y][x] = field; + continue; + } + + if(field == game_state->next_field[next_y][x]) { + game_state->next_field[next_y][x]++; + game_state->score.gameScore += pow(2, game_state->next_field[next_y][x]); + /*if(game_state->next_field[next_y][x] == 11 && !game_state->debug) { + DOLPHIN_DEED(getRandomDeed()); + } // get some xp for making a 2048 tile*/ + next_y++; + continue; + } + + next_y++; + game_state->next_field[next_y][x] = field; + } + } + } + + // right + if(game_state->direction == DirectionRight) { + for(uint8_t y = 0; y < 4; y++) { + uint8_t next_x = 3; + for(int8_t x = 3; x >= 0; x--) { + uint8_t field = game_state->field[y][x]; + if(field == 0) { + continue; + } + + if(game_state->next_field[y][next_x] == 0) { + game_state->next_field[y][next_x] = field; + continue; + } + + if(field == game_state->next_field[y][next_x]) { + game_state->next_field[y][next_x]++; + game_state->score.gameScore += pow(2, game_state->next_field[y][next_x]); + /*if(game_state->next_field[y][next_x] == 11 && !game_state->debug) { + DOLPHIN_DEED(getRandomDeed()); + } // get some xp for making a 2048 tile*/ + next_x--; + continue; + } + + next_x--; + game_state->next_field[y][next_x] = field; + } + } + } + + // down + if(game_state->direction == DirectionDown) { + for(uint8_t x = 0; x < 4; x++) { + uint8_t next_y = 3; + for(int8_t y = 3; y >= 0; y--) { + uint8_t field = game_state->field[y][x]; + if(field == 0) { + continue; + } + + if(game_state->next_field[next_y][x] == 0) { + game_state->next_field[next_y][x] = field; + continue; + } + + if(field == game_state->next_field[next_y][x]) { + game_state->next_field[next_y][x]++; + game_state->score.gameScore += pow(2, game_state->next_field[next_y][x]); + /*if(game_state->next_field[next_y][x] == 11 && !game_state->debug) { + DOLPHIN_DEED(getRandomDeed()); + } // get some xp for making a 2048 tile*/ + next_y--; + continue; + } + + next_y--; + game_state->next_field[next_y][x] = field; + } + } + } + + // 0, 0, 1, 1 + // 1, 0, 0, 0 + + // left + if(game_state->direction == DirectionLeft) { + for(uint8_t y = 0; y < 4; y++) { + uint8_t next_x = 0; + for(uint8_t x = 0; x < 4; x++) { + uint8_t field = game_state->field[y][x]; + if(field == 0) { + continue; + } + + if(game_state->next_field[y][next_x] == 0) { + game_state->next_field[y][next_x] = field; + continue; + } + + if(field == game_state->next_field[y][next_x]) { + game_state->next_field[y][next_x]++; + game_state->score.gameScore += pow(2, game_state->next_field[y][next_x]); + /*if(game_state->next_field[y][next_x] == 11 && !game_state->debug) { + DOLPHIN_DEED(getRandomDeed()); + } // get some xp for making a 2048 tile*/ + next_x++; + continue; + } + + next_x++; + game_state->next_field[y][next_x] = field; + } + } + } + + // + game_state->direction = DirectionIdle; + memcpy(game_state->field, game_state->next_field, sizeof(game_state->field)); + // +} + +static void game_2048_restart(GameState* const game_state) { + game_state->debug = DEBUG; + + // check score + if(game_state->score.gameScore > game_state->score.highScore) { + game_state->score.highScore = game_state->score.gameScore; + } + + // clear all cells + for(uint8_t y = 0; y < 4; y++) { + for(uint8_t x = 0; x < 4; x++) { + game_state->field[y][x] = 0; + } + } + + // start next game + game_state->score.gameScore = 0; + game_2048_set_new_number(game_state); + game_2048_set_new_number(game_state); +} + +int32_t game_2048_app(void* p) { + UNUSED(p); + int32_t return_code = 0; + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + GameState* game_state = malloc(sizeof(GameState)); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) { + return_code = 255; + goto free_and_exit; + } + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set( + view_port, (ViewPortDrawCallback)game_2048_render_callback, &state_mutex); + view_port_input_callback_set( + view_port, (ViewPortInputCallback)game_2048_input_callback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + game_state->direction = DirectionIdle; + game_2048_restart(game_state); + + if(game_state->debug) { + game_state->field[0][0] = 0; + game_state->field[0][1] = 0; + game_state->field[0][2] = 0; + game_state->field[0][3] = 0; + + game_state->field[1][0] = 1; + game_state->field[1][1] = 2; + game_state->field[1][2] = 3; + game_state->field[1][3] = 4; + + game_state->field[2][0] = 5; + game_state->field[2][1] = 6; + game_state->field[2][2] = 7; + game_state->field[2][3] = 8; + + game_state->field[3][0] = 9; + game_state->field[3][1] = 10; + game_state->field[3][2] = 11; + game_state->field[3][3] = 12; + } + + InputEvent event; + for(bool loop = true; loop;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + GameState* game_state = (GameState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + if(event.type == InputTypeShort) { + switch(event.key) { + case InputKeyUp: + game_state->direction = DirectionUp; + game_2048_process_move(game_state); + game_2048_set_new_number(game_state); + break; + case InputKeyDown: + game_state->direction = DirectionDown; + game_2048_process_move(game_state); + game_2048_set_new_number(game_state); + break; + case InputKeyRight: + game_state->direction = DirectionRight; + game_2048_process_move(game_state); + game_2048_set_new_number(game_state); + break; + case InputKeyLeft: + game_state->direction = DirectionLeft; + game_2048_process_move(game_state); + game_2048_set_new_number(game_state); + break; + case InputKeyOk: + game_state->direction = DirectionIdle; + break; + case InputKeyBack: + loop = false; + break; + default: + break; + } + } else if(event.type == InputTypeLong) { + if(event.key == InputKeyOk) { + game_state->direction = DirectionIdle; + game_2048_restart(game_state); + } + } + } + + view_port_update(view_port); + release_mutex(&state_mutex, game_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + delete_mutex(&state_mutex); + +free_and_exit: + free(game_state); + furi_message_queue_free(event_queue); + + return return_code; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/game_of_life/application.fam b/Applications/Official/DEV_FW/source/xMasterX/game_of_life/application.fam new file mode 100644 index 000000000..bc254500b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/game_of_life/application.fam @@ -0,0 +1,12 @@ +App( + appid="GameOfLife", + name="Game of Life", + apptype=FlipperAppType.EXTERNAL, + entry_point="game_of_life_app", + cdefines=["APP_GAMEOFLIFE_GAME"], + requires=["gui"], + stack_size=2 * 1024, + order=110, + fap_icon="golIcon.png", + fap_category="Games_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/game_of_life/game_of_life.c b/Applications/Official/DEV_FW/source/xMasterX/game_of_life/game_of_life.c new file mode 100644 index 000000000..65be8cb72 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/game_of_life/game_of_life.c @@ -0,0 +1,160 @@ +#include +#include + +#include +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define TOTAL_PIXELS SCREEN_WIDTH* SCREEN_HEIGHT + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} AppEvent; + +typedef struct { + bool revive; + int evo; +} State; + +unsigned char new[TOTAL_PIXELS] = {}; +unsigned char old[TOTAL_PIXELS] = {}; +unsigned char* fields[] = {new, old}; + +int current = 0; +int next = 1; + +unsigned char get_cell(int x, int y) { + if(x <= 0 || x >= SCREEN_WIDTH) return 0; + if(y <= 0 || y >= SCREEN_HEIGHT) return 0; + + int pix = (y * SCREEN_WIDTH) + x; + return fields[current][pix]; +} + +int count_neightbors(int x, int y) { + return get_cell(x + 1, y - 1) + get_cell(x - 1, y - 1) + get_cell(x - 1, y + 1) + + get_cell(x + 1, y + 1) + get_cell(x + 1, y) + get_cell(x - 1, y) + get_cell(x, y - 1) + + get_cell(x, y + 1); +} + +static void update_field(State* state) { + if(state->revive) { + for(int i = 0; i < TOTAL_PIXELS; ++i) { + if((random() % 100) == 1) { + fields[current][i] = 1; + } + state->revive = false; + } + } + + for(int i = 0; i < TOTAL_PIXELS; ++i) { + int x = i % SCREEN_WIDTH; + int y = (int)(i / SCREEN_WIDTH); + + int v = get_cell(x, y); + int n = count_neightbors(x, y); + + if(v && n == 3) { + ++state->evo; + } else if(v && (n < 2 || n > 3)) { + ++state->evo; + v = 0; + } else if(!v && n == 3) { + ++state->evo; + v = 1; + } + + fields[next][i] = v; + } + + next ^= current; + current ^= next; + next ^= current; + + if(state->evo < TOTAL_PIXELS) { + state->revive = true; + state->evo = 0; + } +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + AppEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void render_callback(Canvas* canvas, void* ctx) { + State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25); + canvas_clear(canvas); + + for(int i = 0; i < TOTAL_PIXELS; ++i) { + int x = i % SCREEN_WIDTH; + int y = (int)(i / SCREEN_WIDTH); + if(fields[current][i] == 1) canvas_draw_dot(canvas, x, y); + } + release_mutex((ValueMutex*)ctx, state); +} + +int32_t game_of_life_app(void* p) { + UNUSED(p); + srand(DWT->CYCCNT); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(1, sizeof(AppEvent)); + furi_check(event_queue); + + State* _state = malloc(sizeof(State)); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, _state, sizeof(State))) { + printf("cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(_state); + return 255; + } + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + AppEvent event; + for(bool processing = true; processing;) { + State* state = (State*)acquire_mutex_block(&state_mutex); + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); + + if(event_status == FuriStatusOk && event.type == EventTypeKey && + event.input.type == InputTypePress) { + if(event.input.key == InputKeyBack) { + // furiac_exit(NULL); + processing = false; + release_mutex(&state_mutex, state); + break; + } + } + + update_field(state); + + view_port_update(view_port); + release_mutex(&state_mutex, state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + free(_state); + + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/game_of_life/golIcon.png b/Applications/Official/DEV_FW/source/xMasterX/game_of_life/golIcon.png new file mode 100644 index 000000000..df14f812c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/game_of_life/golIcon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/application.fam b/Applications/Official/DEV_FW/source/xMasterX/ifttt/application.fam new file mode 100644 index 000000000..141929645 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/application.fam @@ -0,0 +1,14 @@ +App( + appid="ESP8266_IFTTT_Virtual_Button", + name="[ESP] IFTTT Button", + apptype=FlipperAppType.EXTERNAL, + entry_point="ifttt_virtual_button_app", + cdefines=["APP_IFTTT_VIRTUAL_BUTTON"], + requires=[ + "gui", + ], + stack_size=2 * 1024, + order=20, + fap_icon="icon.png", + fap_category="GPIO_Extra", +) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/icon.png b/Applications/Official/DEV_FW/source/xMasterX/ifttt/icon.png new file mode 100644 index 000000000..f6d586b38 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ifttt/icon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/ifttt_virtual_button.c b/Applications/Official/DEV_FW/source/xMasterX/ifttt/ifttt_virtual_button.c new file mode 100644 index 000000000..e23b8715d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/ifttt_virtual_button.c @@ -0,0 +1,251 @@ +#include "ifttt_virtual_button.h" + +#define IFTTT_FOLDER "/ext/ifttt" +#define IFTTT_CONFIG_FOLDER "/ext/ifttt/config" +const char* CONFIG_FILE_PATH = "/ext/ifttt/config/config.settings"; + +#define FLIPPERZERO_SERIAL_BAUD 115200 +typedef enum ESerialCommand { ESerialCommand_Config } ESerialCommand; + +Settings save_settings(Settings settings) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { + flipper_format_update_string_cstr(file, CONF_SSID, settings.save_ssid); + flipper_format_update_string_cstr(file, CONF_PASSWORD, settings.save_password); + flipper_format_update_string_cstr(file, CONF_KEY, settings.save_key); + flipper_format_update_string_cstr(file, CONF_EVENT, settings.save_event); + } else { + } + flipper_format_file_close(file); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + return settings; +} + +void save_settings_file(FlipperFormat* file, Settings* settings) { + flipper_format_write_header_cstr(file, CONFIG_FILE_HEADER, CONFIG_FILE_VERSION); + flipper_format_write_comment_cstr(file, "Enter here the SSID of the wifi network"); + flipper_format_write_string_cstr(file, CONF_SSID, settings->save_ssid); + flipper_format_write_comment_cstr(file, "Enter here the PASSWORD of the wifi network"); + flipper_format_write_string_cstr(file, CONF_PASSWORD, settings->save_password); + flipper_format_write_comment_cstr(file, "Enter here the WEBHOOKS of your IFTTT account"); + flipper_format_write_string_cstr(file, CONF_KEY, settings->save_key); + flipper_format_write_comment_cstr(file, "Enter here the EVENT name of your trigger"); + flipper_format_write_string_cstr(file, CONF_EVENT, settings->save_event); +} + +Settings* load_settings() { + Settings* settings = malloc(sizeof(Settings)); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + FuriString* string_value; + string_value = furi_string_alloc(); + FuriString* text_ssid_value; + text_ssid_value = furi_string_alloc(); + FuriString* text_password_value; + text_password_value = furi_string_alloc(); + FuriString* text_key_value; + text_key_value = furi_string_alloc(); + FuriString* text_event_value; + text_event_value = furi_string_alloc(); + + if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) != FSE_OK) { + if(!flipper_format_file_open_new(file, CONFIG_FILE_PATH)) { + flipper_format_file_close(file); + } else { + settings->save_ssid = malloc(1); + settings->save_password = malloc(1); + settings->save_key = malloc(1); + settings->save_event = malloc(1); + + settings->save_ssid[0] = '\0'; + settings->save_password[0] = '\0'; + settings->save_key[0] = '\0'; + settings->save_event[0] = '\0'; + + save_settings_file(file, settings); + flipper_format_file_close(file); + } + } else { + if(!flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { + flipper_format_file_close(file); + } else { + uint32_t value; + if(!flipper_format_read_header(file, string_value, &value)) { + } else { + if(flipper_format_read_string(file, CONF_SSID, text_ssid_value)) { + settings->save_ssid = malloc(furi_string_size(text_ssid_value) + 1); + strcpy(settings->save_ssid, furi_string_get_cstr(text_ssid_value)); + } + if(flipper_format_read_string(file, CONF_PASSWORD, text_password_value)) { + settings->save_password = malloc(furi_string_size(text_password_value) + 1); + strcpy(settings->save_password, furi_string_get_cstr(text_password_value)); + } + if(flipper_format_read_string(file, CONF_KEY, text_key_value)) { + settings->save_key = malloc(furi_string_size(text_key_value) + 1); + strcpy(settings->save_key, furi_string_get_cstr(text_key_value)); + } + if(flipper_format_read_string(file, CONF_EVENT, text_event_value)) { + settings->save_event = malloc(furi_string_size(text_event_value) + 1); + strcpy(settings->save_event, furi_string_get_cstr(text_event_value)); + } + } + flipper_format_file_close(file); + } + } + + furi_string_free(text_ssid_value); + furi_string_free(text_password_value); + furi_string_free(text_key_value); + furi_string_free(text_event_value); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + return settings; +} + +void send_serial_command_config(ESerialCommand command, Settings* settings) { + uint8_t data[1] = {0}; + + char config_tmp[100]; + strcpy(config_tmp, "config,"); + strcat(config_tmp, settings->save_key); + char config_tmp2[5]; + strcpy(config_tmp2, config_tmp); + strcat(config_tmp2, ","); + char config_tmp3[100]; + strcpy(config_tmp3, config_tmp2); + strcat(config_tmp3, settings->save_ssid); + char config_tmp4[5]; + strcpy(config_tmp4, config_tmp3); + strcat(config_tmp4, ","); + char config_tmp5[100]; + strcpy(config_tmp5, config_tmp4); + strcat(config_tmp5, settings->save_password); + char config_tmp6[5]; + strcpy(config_tmp6, config_tmp5); + strcat(config_tmp6, ","); + char config[350]; + strcpy(config, config_tmp6); + strcat(config, settings->save_event); + + int length = strlen(config); + for(int i = 0; i < length; i++) { + switch(command) { + case ESerialCommand_Config: + data[0] = config[i]; + break; + default: + return; + }; + + furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); + } +} + +static bool ifttt_virtual_button_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + VirtualButtonApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool ifttt_virtual_button_back_event_callback(void* context) { + furi_assert(context); + VirtualButtonApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void ifttt_virtual_button_tick_event_callback(void* context) { + furi_assert(context); + VirtualButtonApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +VirtualButtonApp* ifttt_virtual_button_app_alloc(uint32_t first_scene) { + VirtualButtonApp* app = malloc(sizeof(VirtualButtonApp)); + + // Records + app->gui = furi_record_open(RECORD_GUI); + app->power = furi_record_open(RECORD_POWER); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&virtual_button_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, ifttt_virtual_button_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, ifttt_virtual_button_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, ifttt_virtual_button_tick_event_callback, 2000); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Views + app->sen_view = send_view_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, VirtualButtonAppViewSendView, send_view_get_view(app->sen_view)); + + app->abou_view = about_view_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, VirtualButtonAppViewAboutView, about_view_get_view(app->abou_view)); + + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, VirtualButtonAppViewSubmenu, submenu_get_view(app->submenu)); + app->dialog = dialog_ex_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, VirtualButtonAppViewDialog, dialog_ex_get_view(app->dialog)); + + // Set first scene + scene_manager_next_scene(app->scene_manager, first_scene); + return app; +} + +void ifttt_virtual_button_app_free(VirtualButtonApp* app) { + furi_assert(app); + + free(app->settings.save_ssid); + free(app->settings.save_password); + free(app->settings.save_key); + + // Views + view_dispatcher_remove_view(app->view_dispatcher, VirtualButtonAppViewSendView); + send_view_free(app->sen_view); + view_dispatcher_remove_view(app->view_dispatcher, VirtualButtonAppViewAboutView); + about_view_free(app->abou_view); + view_dispatcher_remove_view(app->view_dispatcher, VirtualButtonAppViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, VirtualButtonAppViewDialog); + dialog_ex_free(app->dialog); + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + // Records + furi_record_close(RECORD_POWER); + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t ifttt_virtual_button_app(void* p) { + UNUSED(p); + + Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage_simply_mkdir(storage, IFTTT_FOLDER)) { + } + if(!storage_simply_mkdir(storage, IFTTT_CONFIG_FOLDER)) { + } + furi_record_close(RECORD_STORAGE); + + uint32_t first_scene = VirtualButtonAppSceneStart; + VirtualButtonApp* app = ifttt_virtual_button_app_alloc(first_scene); + memcpy(&app->settings, load_settings(), sizeof(Settings)); + send_serial_command_config(ESerialCommand_Config, &(app->settings)); + + view_dispatcher_run(app->view_dispatcher); + ifttt_virtual_button_app_free(app); + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/ifttt_virtual_button.h b/Applications/Official/DEV_FW/source/xMasterX/ifttt/ifttt_virtual_button.h new file mode 100644 index 000000000..563f5cd95 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/ifttt_virtual_button.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "views/send_view.h" +#include "views/about_view.h" +#include +#include +#include +#include +#include +#include +#include "scenes/virtual_button_scene.h" + +#define APP_NAME "[ESP8266] IFTTT Virtual Button" + +#define CONF_SSID "wifi_ssid" +#define CONF_PASSWORD "wifi_password" +#define CONF_KEY "webhooks_key" +#define CONF_EVENT "event" +#define CONFIG_FILE_HEADER "IFTTT Virtual Button Config File" +#define CONFIG_FILE_VERSION 1 + +typedef struct { + char* save_ssid; + char* save_password; + char* save_key; + char* save_event; +} Settings; + +typedef struct { + Power* power; + Gui* gui; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + SendView* sen_view; + AboutView* abou_view; + Submenu* submenu; + DialogEx* dialog; + PowerInfo info; + Settings settings; +} VirtualButtonApp; + +typedef enum { + VirtualButtonAppViewSendView, + VirtualButtonAppViewAboutView, + VirtualButtonAppViewSubmenu, + VirtualButtonAppViewDialog, +} VirtualButtonAppView; + +Settings save_settings(Settings settings); +Settings* load_settings(); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene.c b/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene.c new file mode 100644 index 000000000..a75d822fc --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene.c @@ -0,0 +1,30 @@ +#include "virtual_button_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const virtual_button_on_enter_handlers[])(void*) = { +#include "virtual_button_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const virtual_button_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "virtual_button_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const virtual_button_on_exit_handlers[])(void* context) = { +#include "virtual_button_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers virtual_button_scene_handlers = { + .on_enter_handlers = virtual_button_on_enter_handlers, + .on_event_handlers = virtual_button_on_event_handlers, + .on_exit_handlers = virtual_button_on_exit_handlers, + .scene_num = VirtualButtonAppSceneNum, +}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene.h b/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene.h new file mode 100644 index 000000000..870807dee --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) VirtualButtonAppScene##id, +typedef enum { +#include "virtual_button_scene_config.h" + VirtualButtonAppSceneNum, +} VirtualButtonAppScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers virtual_button_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "virtual_button_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "virtual_button_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "virtual_button_scene_config.h" +#undef ADD_SCENE diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene_about.c b/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene_about.c new file mode 100644 index 000000000..86fe1a9d0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene_about.c @@ -0,0 +1,26 @@ +#include "../ifttt_virtual_button.h" + +static void virtual_button_scene_about_view_update_model(VirtualButtonApp* app) { + power_get_info(app->power, &app->info); +} + +void virtual_button_scene_about_view_on_enter(void* context) { + VirtualButtonApp* app = context; + virtual_button_scene_about_view_update_model(app); + view_dispatcher_switch_to_view(app->view_dispatcher, VirtualButtonAppViewAboutView); +} + +bool virtual_button_scene_about_view_on_event(void* context, SceneManagerEvent event) { + VirtualButtonApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + virtual_button_scene_about_view_update_model(app); + consumed = true; + } + return consumed; +} + +void virtual_button_scene_about_view_on_exit(void* context) { + UNUSED(context); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene_config.h b/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene_config.h new file mode 100644 index 000000000..70af5ccf7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(virtual_button, start, Start) +ADD_SCENE(virtual_button, send_view, SendView) +ADD_SCENE(virtual_button, about_view, AboutView) diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene_send.c b/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene_send.c new file mode 100644 index 000000000..caa23fadf --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene_send.c @@ -0,0 +1,26 @@ +#include "../ifttt_virtual_button.h" + +static void virtual_button_scene_send_view_update_model(VirtualButtonApp* app) { + power_get_info(app->power, &app->info); +} + +void virtual_button_scene_send_view_on_enter(void* context) { + VirtualButtonApp* app = context; + virtual_button_scene_send_view_update_model(app); + view_dispatcher_switch_to_view(app->view_dispatcher, VirtualButtonAppViewSendView); +} + +bool virtual_button_scene_send_view_on_event(void* context, SceneManagerEvent event) { + VirtualButtonApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + virtual_button_scene_send_view_update_model(app); + consumed = true; + } + return consumed; +} + +void virtual_button_scene_send_view_on_exit(void* context) { + UNUSED(context); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene_start.c b/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene_start.c new file mode 100644 index 000000000..6b03a35f0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/scenes/virtual_button_scene_start.c @@ -0,0 +1,55 @@ +#include "../ifttt_virtual_button.h" + +enum VirtualButtonSubmenuIndex { + VirtualButtonSubmenuIndexSendView, + VirtualButtonSubmenuIndexAboutView, +}; + +static void virtual_button_scene_start_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + VirtualButtonApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void virtual_button_scene_start_on_enter(void* context) { + VirtualButtonApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Send IFTTT command", + VirtualButtonSubmenuIndexSendView, + virtual_button_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "About", + VirtualButtonSubmenuIndexAboutView, + virtual_button_scene_start_submenu_callback, + app); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, VirtualButtonAppSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, VirtualButtonAppViewSubmenu); +} + +bool virtual_button_scene_start_on_event(void* context, SceneManagerEvent event) { + VirtualButtonApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == VirtualButtonSubmenuIndexSendView) { + scene_manager_next_scene(app->scene_manager, VirtualButtonAppSceneSendView); + } else if(event.event == VirtualButtonSubmenuIndexAboutView) { + scene_manager_next_scene(app->scene_manager, VirtualButtonAppSceneAboutView); + } + scene_manager_set_scene_state(app->scene_manager, VirtualButtonAppSceneStart, event.event); + consumed = true; + } + return consumed; +} + +void virtual_button_scene_start_on_exit(void* context) { + VirtualButtonApp* app = context; + submenu_reset(app->submenu); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/views/about_view.c b/Applications/Official/DEV_FW/source/xMasterX/ifttt/views/about_view.c new file mode 100644 index 000000000..80c00883a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/views/about_view.c @@ -0,0 +1,48 @@ +#include "about_view.h" +#include +#include +#include +#include + +struct AboutView { + View* view; +}; + +typedef struct { + bool connected; +} AboutViewModel; + +static void about_view_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "IFTTT Virtual button"); + canvas_draw_str_aligned(canvas, 0, 15, AlignLeft, AlignTop, "Version 0.2"); + canvas_draw_str_aligned(canvas, 0, 50, AlignLeft, AlignTop, "press back"); +} + +AboutView* about_view_alloc() { + AboutView* about_view = malloc(sizeof(AboutView)); + about_view->view = view_alloc(); + view_set_context(about_view->view, about_view); + view_allocate_model(about_view->view, ViewModelTypeLocking, sizeof(AboutViewModel)); + view_set_draw_callback(about_view->view, about_view_draw_callback); + return about_view; +} + +void about_view_free(AboutView* about_view) { + furi_assert(about_view); + view_free(about_view->view); + free(about_view); +} + +View* about_view_get_view(AboutView* about_view) { + furi_assert(about_view); + return about_view->view; +} + +void about_view_set_data(AboutView* about_view, bool connected) { + furi_assert(about_view); + with_view_model( + about_view->view, AboutViewModel * model, { model->connected = connected; }, true); +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/views/about_view.h b/Applications/Official/DEV_FW/source/xMasterX/ifttt/views/about_view.h new file mode 100644 index 000000000..d1ac287e3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/views/about_view.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +typedef struct AboutView AboutView; + +AboutView* about_view_alloc(); + +void about_view_free(AboutView* about_view); + +View* about_view_get_view(AboutView* about_view); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/views/send_view.c b/Applications/Official/DEV_FW/source/xMasterX/ifttt/views/send_view.c new file mode 100644 index 000000000..6046c39e3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/views/send_view.c @@ -0,0 +1,137 @@ +#include "send_view.h" +#include +#include +#include +#include +#include +#include +#include + +#define FLIPPERZERO_SERIAL_BAUD 115200 + +typedef enum ESerialCommand { ESerialCommand_Send } ESerialCommand; + +struct SendView { + View* view; +}; + +typedef struct { + bool right_pressed; + bool connected; +} SendViewModel; + +static void Shake(void) { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_single_vibro); + furi_record_close(RECORD_NOTIFICATION); +} + +void send_serial_command_send(ESerialCommand command) { + uint8_t data[1] = {0}; + + char name[10] = "send"; + int length = strlen(name); + for(int i = 0; i < length; i++) { + switch(command) { + case ESerialCommand_Send: + data[0] = name[i]; + break; + default: + return; + }; + + furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); + } +} + +static void send_view_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + SendViewModel* model = context; + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, "SEND MODULE"); + canvas_draw_line(canvas, 0, 10, 128, 10); + canvas_draw_str_aligned(canvas, 64, 15, AlignCenter, AlignTop, "Press right to send IFTTT"); + canvas_draw_str_aligned(canvas, 64, 25, AlignCenter, AlignTop, "command or press and hold"); + canvas_draw_str_aligned(canvas, 64, 35, AlignCenter, AlignTop, "back to return to the menu"); + + // Right + if(model->right_pressed) { + } +} + +static void send_view_process(SendView* send_view, InputEvent* event) { + with_view_model( + send_view->view, + SendViewModel * model, + { + if(event->type == InputTypePress) { + if(event->key == InputKeyUp) { + } else if(event->key == InputKeyDown) { + } else if(event->key == InputKeyLeft) { + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + Shake(); + send_serial_command_send(ESerialCommand_Send); + } else if(event->key == InputKeyOk) { + } else if(event->key == InputKeyBack) { + } + } else if(event->type == InputTypeRelease) { + if(event->key == InputKeyUp) { + } else if(event->key == InputKeyDown) { + } else if(event->key == InputKeyLeft) { + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + } else if(event->key == InputKeyOk) { + } else if(event->key == InputKeyBack) { + } + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyBack) { + } + } + }, + true); +} + +static bool send_view_input_callback(InputEvent* event, void* context) { + furi_assert(context); + SendView* send_view = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + } else { + send_view_process(send_view, event); + consumed = true; + } + + return consumed; +} + +SendView* send_view_alloc() { + SendView* send_view = malloc(sizeof(SendView)); + send_view->view = view_alloc(); + view_set_context(send_view->view, send_view); + view_allocate_model(send_view->view, ViewModelTypeLocking, sizeof(SendViewModel)); + view_set_draw_callback(send_view->view, send_view_draw_callback); + view_set_input_callback(send_view->view, send_view_input_callback); + furi_hal_uart_set_br(FuriHalUartIdUSART1, FLIPPERZERO_SERIAL_BAUD); + + return send_view; +} + +void send_view_free(SendView* send_view) { + furi_assert(send_view); + view_free(send_view->view); + free(send_view); +} + +View* send_view_get_view(SendView* send_view) { + furi_assert(send_view); + return send_view->view; +} + +void send_view_set_data(SendView* send_view, bool connected) { + furi_assert(send_view); + with_view_model( + send_view->view, SendViewModel * model, { model->connected = connected; }, true); +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/ifttt/views/send_view.h b/Applications/Official/DEV_FW/source/xMasterX/ifttt/views/send_view.h new file mode 100644 index 000000000..4b1944dd4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ifttt/views/send_view.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +typedef struct SendView SendView; + +SendView* send_view_alloc(); + +void send_view_free(SendView* send_view); + +View* send_view_get_view(SendView* send_view); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/README.md b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/README.md new file mode 100644 index 000000000..f1a3161db --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/README.md @@ -0,0 +1,63 @@ +# Alternative Infrared Remote for Flipperzero + +It is a plugin like [UniversalRF Remix](https://github.com/ESurge/flipperzero-firmware-unirfremix) but for infrared files. I do this plugin for convenience, because the main IR app need to navigate for different button abit troublesome (buttons like up,down,left,right,back). I found it useful for TV and TV box. + +It supports short press and long press input for different ir remote buttons. Tested on the [unleashed firmware version unlshd-023](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/unlshd-023) + + +## How to install + +1. Update unleashed firmware to the version unlshd-023, then download the `ir_remote.fap` from [releases](https://github.com/Hong5489/ir_remote/tags) + +2. Put the `ir_remote.fap` file in your flipper's SD card, under `apps` folder + +## How to use + +1. Similar to UniRF app, put the path of the ir file and the ir button for each button on flipper (UP,DOWN,LEFT,RIGHT,BACK) + +The format With `HOLD` one is long press, without is short press + +Example of the configuration file: +``` +REMOTE: /ext/infrared/Philips_32PFL4208T.ir +UP: Up +DOWN: Down +LEFT: Left +RIGHT: Right +OK: +BACK: Back +UPHOLD: VOL+ +DOWNHOLD: VOL- +LEFTHOLD: Source +RIGHTHOLD: SmartTV +OKHOLD: POWER +``` + +Leave it empty for the button you don't need + +2. Save it as `.txt` file, then create a new folder in your SD card `ir_remote`, put it inside the folder + +3. Lastly, you can open the app, choose the configuration file, then you can try out the ir for each buttons + +4. Long press back button to exit the app + +## How to build + +You can clone this repo and put it inside the `applications_user` folder, then build it with the command: +``` +./fbt fap_ir_remote +``` +Or you can build and run it on your flipper with the command: +``` +./fbt launch_app APPSRC=applications_user/ir_remote +``` + +## Screenshots + +Choose config file to map + +![image](ir.png) + +Show all button name in the config file (If empty will show N/A). Upper part short press, Lower part long press + +![image2](ir2.png) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/application.fam b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/application.fam new file mode 100644 index 000000000..cd3f33da9 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/application.fam @@ -0,0 +1,14 @@ +App( + appid="ir_remote", + name="IR Remote", + apptype=FlipperAppType.EXTERNAL, + entry_point="infrared_remote_app", + stack_size=3 * 1024, + requires=[ + "gui", + "dialogs", + ], + fap_category="Misc_Extra", + fap_icon="ir_10px.png", + fap_icon_assets="images", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/example.txt b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/example.txt new file mode 100644 index 000000000..ffd192b8d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/example.txt @@ -0,0 +1,12 @@ +REMOTE: /ext/infrared/Philips_32PFL4208T.ir +UP: Up +DOWN: Down +LEFT: Left +RIGHT: Right +OK: +BACK: Back +UPHOLD: VOL+ +DOWNHOLD: VOL- +LEFTHOLD: Source +RIGHTHOLD: SmartTV +OKHOLD: POWER \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/ButtonDown_7x4.png b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/ButtonDown_7x4.png new file mode 100644 index 000000000..2954bb6a6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/ButtonDown_7x4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/ButtonLeft_4x7.png b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/ButtonLeft_4x7.png new file mode 100644 index 000000000..0b4655d43 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/ButtonLeft_4x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/ButtonRight_4x7.png b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/ButtonRight_4x7.png new file mode 100644 index 000000000..8e1c74c1c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/ButtonRight_4x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/ButtonUp_7x4.png b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/ButtonUp_7x4.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/ButtonUp_7x4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/Ok_btn_9x9.png b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/Ok_btn_9x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/back_10px.png b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/back_10px.png new file mode 100644 index 000000000..f9c615a99 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/back_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/sub1_10px.png b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/sub1_10px.png new file mode 100644 index 000000000..5a25fdf4e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/images/sub1_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote.c b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote.c new file mode 100644 index 000000000..3a528a656 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote.c @@ -0,0 +1,188 @@ +#include "infrared_remote.h" + +#include +#include +#include +#include +#include +#include +#include + +#define TAG "InfraredRemote" + +ARRAY_DEF(InfraredButtonArray, InfraredRemoteButton*, M_PTR_OPLIST); + +struct InfraredRemote { + InfraredButtonArray_t buttons; + FuriString* name; + FuriString* path; +}; + +static void infrared_remote_clear_buttons(InfraredRemote* remote) { + InfraredButtonArray_it_t it; + for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it); + InfraredButtonArray_next(it)) { + infrared_remote_button_free(*InfraredButtonArray_cref(it)); + } + InfraredButtonArray_reset(remote->buttons); +} + +InfraredRemote* infrared_remote_alloc() { + InfraredRemote* remote = malloc(sizeof(InfraredRemote)); + InfraredButtonArray_init(remote->buttons); + remote->name = furi_string_alloc(); + remote->path = furi_string_alloc(); + return remote; +} + +void infrared_remote_free(InfraredRemote* remote) { + infrared_remote_clear_buttons(remote); + InfraredButtonArray_clear(remote->buttons); + furi_string_free(remote->path); + furi_string_free(remote->name); + free(remote); +} + +void infrared_remote_reset(InfraredRemote* remote) { + infrared_remote_clear_buttons(remote); + furi_string_reset(remote->name); + furi_string_reset(remote->path); +} + +void infrared_remote_set_name(InfraredRemote* remote, const char* name) { + furi_string_set(remote->name, name); +} + +const char* infrared_remote_get_name(InfraredRemote* remote) { + return furi_string_get_cstr(remote->name); +} + +void infrared_remote_set_path(InfraredRemote* remote, const char* path) { + furi_string_set(remote->path, path); +} + +const char* infrared_remote_get_path(InfraredRemote* remote) { + return furi_string_get_cstr(remote->path); +} + +size_t infrared_remote_get_button_count(InfraredRemote* remote) { + return InfraredButtonArray_size(remote->buttons); +} + +InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index) { + furi_assert(index < InfraredButtonArray_size(remote->buttons)); + return *InfraredButtonArray_get(remote->buttons, index); +} + +bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index) { + for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) { + InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i); + if(!strcmp(infrared_remote_button_get_name(button), name)) { + *index = i; + return true; + } + } + return false; +} + +bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal) { + InfraredRemoteButton* button = infrared_remote_button_alloc(); + infrared_remote_button_set_name(button, name); + infrared_remote_button_set_signal(button, signal); + InfraredButtonArray_push_back(remote->buttons, button); + return infrared_remote_store(remote); +} + +bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index) { + furi_assert(index < InfraredButtonArray_size(remote->buttons)); + InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, index); + infrared_remote_button_set_name(button, new_name); + return infrared_remote_store(remote); +} + +bool infrared_remote_delete_button(InfraredRemote* remote, size_t index) { + furi_assert(index < InfraredButtonArray_size(remote->buttons)); + InfraredRemoteButton* button; + InfraredButtonArray_pop_at(&button, remote->buttons, index); + infrared_remote_button_free(button); + return infrared_remote_store(remote); +} + +bool infrared_remote_store(InfraredRemote* remote) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_file_alloc(storage); + const char* path = furi_string_get_cstr(remote->path); + + FURI_LOG_I(TAG, "store file: \'%s\'", path); + + bool success = flipper_format_file_open_always(ff, path) && + flipper_format_write_header_cstr(ff, "IR signals file", 1); + if(success) { + InfraredButtonArray_it_t it; + for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it); + InfraredButtonArray_next(it)) { + InfraredRemoteButton* button = *InfraredButtonArray_cref(it); + success = infrared_signal_save( + infrared_remote_button_get_signal(button), + ff, + infrared_remote_button_get_name(button)); + if(!success) { + break; + } + } + } + + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + return success; +} + +bool infrared_remote_load(InfraredRemote* remote, FuriString* path) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + FuriString* buf; + buf = furi_string_alloc(); + + FURI_LOG_I(TAG, "load file: \'%s\'", furi_string_get_cstr(path)); + bool success = flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(path)); + + if(success) { + uint32_t version; + success = flipper_format_read_header(ff, buf, &version) && + !furi_string_cmp(buf, "IR signals file") && (version == 1); + } + + if(success) { + path_extract_filename(path, buf, true); + infrared_remote_clear_buttons(remote); + infrared_remote_set_name(remote, furi_string_get_cstr(buf)); + infrared_remote_set_path(remote, furi_string_get_cstr(path)); + + for(bool can_read = true; can_read;) { + InfraredRemoteButton* button = infrared_remote_button_alloc(); + can_read = infrared_signal_read(infrared_remote_button_get_signal(button), ff, buf); + if(can_read) { + infrared_remote_button_set_name(button, furi_string_get_cstr(buf)); + InfraredButtonArray_push_back(remote->buttons, button); + } else { + infrared_remote_button_free(button); + } + } + } + + furi_string_free(buf); + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + return success; +} + +bool infrared_remote_remove(InfraredRemote* remote) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + FS_Error status = storage_common_remove(storage, furi_string_get_cstr(remote->path)); + infrared_remote_reset(remote); + + furi_record_close(RECORD_STORAGE); + return (status == FSE_OK || status == FSE_NOT_EXIST); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote.h b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote.h new file mode 100644 index 000000000..6eac193d3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "infrared_remote_button.h" + +typedef struct InfraredRemote InfraredRemote; + +InfraredRemote* infrared_remote_alloc(); +void infrared_remote_free(InfraredRemote* remote); +void infrared_remote_reset(InfraredRemote* remote); + +void infrared_remote_set_name(InfraredRemote* remote, const char* name); +const char* infrared_remote_get_name(InfraredRemote* remote); + +void infrared_remote_set_path(InfraredRemote* remote, const char* path); +const char* infrared_remote_get_path(InfraredRemote* remote); + +size_t infrared_remote_get_button_count(InfraredRemote* remote); +InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index); +bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index); + +bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal); +bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index); +bool infrared_remote_delete_button(InfraredRemote* remote, size_t index); + +bool infrared_remote_store(InfraredRemote* remote); +bool infrared_remote_load(InfraredRemote* remote, FuriString* path); +bool infrared_remote_remove(InfraredRemote* remote); diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote_app.c b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote_app.c new file mode 100644 index 000000000..93105a8b2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote_app.c @@ -0,0 +1,532 @@ +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "infrared_signal.h" +#include "infrared_remote.h" +#include "infrared_remote_button.h" +#define TAG "ir_remote" + +#include + +typedef struct { + int status; + ViewPort* view_port; + FuriString* up_button; + FuriString* down_button; + FuriString* left_button; + FuriString* right_button; + FuriString* ok_button; + FuriString* back_button; + FuriString* up_hold_button; + FuriString* down_hold_button; + FuriString* left_hold_button; + FuriString* right_hold_button; + FuriString* ok_hold_button; +} IRApp; + +// Screen is 128x64 px +static void app_draw_callback(Canvas* canvas, void* ctx) { + // Show config is incorrect when cannot read the remote file + // Showing button string in the screen, upper part is short press, lower part is long press + IRApp* app = ctx; + if(app->status) { + canvas_clear(canvas); + view_port_set_orientation(app->view_port, ViewPortOrientationHorizontal); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 62, 5, AlignCenter, AlignTop, "Config is incorrect."); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignTop, "Please configure map."); + canvas_draw_str_aligned(canvas, 62, 60, AlignCenter, AlignBottom, "Press Back to Exit."); + } else { + canvas_clear(canvas); + view_port_set_orientation(app->view_port, ViewPortOrientationVertical); + canvas_draw_icon(canvas, 1, 5, &I_ButtonUp_7x4); + canvas_draw_icon(canvas, 1, 15, &I_ButtonDown_7x4); + canvas_draw_icon(canvas, 2, 23, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 2, 33, &I_ButtonRight_4x7); + canvas_draw_icon(canvas, 0, 42, &I_Ok_btn_9x9); + canvas_draw_icon(canvas, 0, 53, &I_back_10px); + + //Labels + canvas_set_font(canvas, FontSecondary); + + canvas_draw_str_aligned( + canvas, 32, 8, AlignCenter, AlignCenter, furi_string_get_cstr(app->up_button)); + canvas_draw_str_aligned( + canvas, 32, 18, AlignCenter, AlignCenter, furi_string_get_cstr(app->down_button)); + canvas_draw_str_aligned( + canvas, 32, 28, AlignCenter, AlignCenter, furi_string_get_cstr(app->left_button)); + canvas_draw_str_aligned( + canvas, 32, 38, AlignCenter, AlignCenter, furi_string_get_cstr(app->right_button)); + canvas_draw_str_aligned( + canvas, 32, 48, AlignCenter, AlignCenter, furi_string_get_cstr(app->ok_button)); + canvas_draw_str_aligned( + canvas, 32, 58, AlignCenter, AlignCenter, furi_string_get_cstr(app->back_button)); + + canvas_draw_line(canvas, 0, 65, 64, 65); + + canvas_draw_icon(canvas, 1, 70, &I_ButtonUp_7x4); + canvas_draw_icon(canvas, 1, 80, &I_ButtonDown_7x4); + canvas_draw_icon(canvas, 2, 88, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 2, 98, &I_ButtonRight_4x7); + canvas_draw_icon(canvas, 0, 107, &I_Ok_btn_9x9); + canvas_draw_icon(canvas, 0, 118, &I_back_10px); + + canvas_draw_str_aligned( + canvas, 32, 73, AlignCenter, AlignCenter, furi_string_get_cstr(app->up_hold_button)); + canvas_draw_str_aligned( + canvas, 32, 83, AlignCenter, AlignCenter, furi_string_get_cstr(app->down_hold_button)); + canvas_draw_str_aligned( + canvas, 32, 93, AlignCenter, AlignCenter, furi_string_get_cstr(app->left_hold_button)); + canvas_draw_str_aligned( + canvas, + 32, + 103, + AlignCenter, + AlignCenter, + furi_string_get_cstr(app->right_hold_button)); + canvas_draw_str_aligned( + canvas, 32, 113, AlignCenter, AlignCenter, furi_string_get_cstr(app->ok_hold_button)); + canvas_draw_str_aligned(canvas, 32, 123, AlignCenter, AlignCenter, "Exit App"); + } +} + +static void app_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t infrared_remote_app(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + // App button string + IRApp* app = malloc(sizeof(IRApp)); + app->up_button = furi_string_alloc(); + app->down_button = furi_string_alloc(); + app->left_button = furi_string_alloc(); + app->right_button = furi_string_alloc(); + app->ok_button = furi_string_alloc(); + app->back_button = furi_string_alloc(); + app->up_hold_button = furi_string_alloc(); + app->down_hold_button = furi_string_alloc(); + app->left_hold_button = furi_string_alloc(); + app->right_hold_button = furi_string_alloc(); + app->ok_hold_button = furi_string_alloc(); + app->view_port = view_port_alloc(); + + // Configure view port + view_port_draw_callback_set(app->view_port, app_draw_callback, app); + view_port_input_callback_set(app->view_port, app_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, app->view_port, GuiLayerFullscreen); + + InputEvent event; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_file_alloc(storage); + + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".txt", &I_sub1_10px); + FuriString* map_file = furi_string_alloc(); + furi_string_set(map_file, "/ext/ir_remote"); + + bool res = dialog_file_browser_show(dialogs, map_file, map_file, &browser_options); + + furi_record_close(RECORD_DIALOGS); + + // if user didn't choose anything, free everything and exit + if(!res) { + FURI_LOG_I(TAG, "exit"); + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + furi_string_free(app->up_button); + furi_string_free(app->down_button); + furi_string_free(app->left_button); + furi_string_free(app->right_button); + furi_string_free(app->ok_button); + furi_string_free(app->back_button); + furi_string_free(app->up_hold_button); + furi_string_free(app->down_hold_button); + furi_string_free(app->left_hold_button); + furi_string_free(app->right_hold_button); + furi_string_free(app->ok_hold_button); + + view_port_enabled_set(app->view_port, false); + gui_remove_view_port(gui, app->view_port); + view_port_free(app->view_port); + free(app); + furi_message_queue_free(event_queue); + + furi_record_close(RECORD_GUI); + return 255; + } + + InfraredRemote* remote = infrared_remote_alloc(); + FuriString* remote_path = furi_string_alloc(); + + InfraredSignal* up_signal = infrared_signal_alloc(); + InfraredSignal* down_signal = infrared_signal_alloc(); + InfraredSignal* left_signal = infrared_signal_alloc(); + InfraredSignal* right_signal = infrared_signal_alloc(); + InfraredSignal* ok_signal = infrared_signal_alloc(); + InfraredSignal* back_signal = infrared_signal_alloc(); + InfraredSignal* up_hold_signal = infrared_signal_alloc(); + InfraredSignal* down_hold_signal = infrared_signal_alloc(); + InfraredSignal* left_hold_signal = infrared_signal_alloc(); + InfraredSignal* right_hold_signal = infrared_signal_alloc(); + InfraredSignal* ok_hold_signal = infrared_signal_alloc(); + + bool up_enabled = false; + bool down_enabled = false; + bool left_enabled = false; + bool right_enabled = false; + bool ok_enabled = false; + bool back_enabled = false; + bool up_hold_enabled = false; + bool down_hold_enabled = false; + bool left_hold_enabled = false; + bool right_hold_enabled = false; + bool ok_hold_enabled = false; + + if(!flipper_format_file_open_existing(ff, furi_string_get_cstr(map_file))) { + FURI_LOG_E(TAG, "Could not open MAP file %s", furi_string_get_cstr(map_file)); + app->status = 1; + } else { + //Filename Assignment/Check Start + + if(!flipper_format_read_string(ff, "REMOTE", remote_path)) { + FURI_LOG_E(TAG, "Could not read REMOTE string"); + app->status = 1; + } else { + if(!infrared_remote_load(remote, remote_path)) { + FURI_LOG_E(TAG, "Could not load ir file: %s", furi_string_get_cstr(remote_path)); + app->status = 1; + } else { + FURI_LOG_I(TAG, "Loaded REMOTE file: %s", furi_string_get_cstr(remote_path)); + } + } + + //assign variables to values within map file + //set missing filenames to N/A + //assign button signals + size_t index = 0; + if(!flipper_format_read_string(ff, "UP", app->up_button)) { + FURI_LOG_W(TAG, "Could not read UP string"); + furi_string_set(app->up_button, "N/A"); + } else { + if(!infrared_remote_find_button_by_name( + remote, furi_string_get_cstr(app->up_button), &index)) { + FURI_LOG_W(TAG, "Error"); + } else { + up_signal = + infrared_remote_button_get_signal(infrared_remote_get_button(remote, index)); + up_enabled = true; + } + } + + if(!flipper_format_read_string(ff, "DOWN", app->down_button)) { + FURI_LOG_W(TAG, "Could not read DOWN string"); + furi_string_set(app->down_button, "N/A"); + } else { + if(!infrared_remote_find_button_by_name( + remote, furi_string_get_cstr(app->down_button), &index)) { + FURI_LOG_W(TAG, "Error"); + } else { + down_signal = + infrared_remote_button_get_signal(infrared_remote_get_button(remote, index)); + down_enabled = true; + } + } + + if(!flipper_format_read_string(ff, "LEFT", app->left_button)) { + FURI_LOG_W(TAG, "Could not read LEFT string"); + furi_string_set(app->left_button, "N/A"); + } else { + if(!infrared_remote_find_button_by_name( + remote, furi_string_get_cstr(app->left_button), &index)) { + FURI_LOG_W(TAG, "Error"); + } else { + left_signal = + infrared_remote_button_get_signal(infrared_remote_get_button(remote, index)); + left_enabled = true; + } + } + + if(!flipper_format_read_string(ff, "RIGHT", app->right_button)) { + FURI_LOG_W(TAG, "Could not read RIGHT string"); + furi_string_set(app->right_button, "N/A"); + } else { + if(!infrared_remote_find_button_by_name( + remote, furi_string_get_cstr(app->right_button), &index)) { + FURI_LOG_W(TAG, "Error"); + } else { + right_signal = + infrared_remote_button_get_signal(infrared_remote_get_button(remote, index)); + right_enabled = true; + } + } + + if(!flipper_format_read_string(ff, "OK", app->ok_button)) { + FURI_LOG_W(TAG, "Could not read OK string"); + furi_string_set(app->ok_button, "N/A"); + } else { + if(!infrared_remote_find_button_by_name( + remote, furi_string_get_cstr(app->ok_button), &index)) { + FURI_LOG_W(TAG, "Error"); + } else { + ok_signal = + infrared_remote_button_get_signal(infrared_remote_get_button(remote, index)); + ok_enabled = true; + } + } + + if(!flipper_format_read_string(ff, "BACK", app->back_button)) { + FURI_LOG_W(TAG, "Could not read BACK string"); + furi_string_set(app->back_button, "N/A"); + } else { + if(!infrared_remote_find_button_by_name( + remote, furi_string_get_cstr(app->back_button), &index)) { + FURI_LOG_W(TAG, "Error"); + } else { + back_signal = + infrared_remote_button_get_signal(infrared_remote_get_button(remote, index)); + back_enabled = true; + } + } + + if(!flipper_format_read_string(ff, "UPHOLD", app->up_hold_button)) { + FURI_LOG_W(TAG, "Could not read UPHOLD string"); + furi_string_set(app->up_hold_button, "N/A"); + } else { + if(!infrared_remote_find_button_by_name( + remote, furi_string_get_cstr(app->up_hold_button), &index)) { + FURI_LOG_W(TAG, "Error"); + } else { + up_hold_signal = + infrared_remote_button_get_signal(infrared_remote_get_button(remote, index)); + up_hold_enabled = true; + } + } + + if(!flipper_format_read_string(ff, "DOWNHOLD", app->down_hold_button)) { + FURI_LOG_W(TAG, "Could not read DOWNHOLD string"); + furi_string_set(app->down_hold_button, "N/A"); + } else { + if(!infrared_remote_find_button_by_name( + remote, furi_string_get_cstr(app->down_hold_button), &index)) { + FURI_LOG_W(TAG, "Error"); + } else { + down_hold_signal = + infrared_remote_button_get_signal(infrared_remote_get_button(remote, index)); + down_hold_enabled = true; + } + } + + if(!flipper_format_read_string(ff, "LEFTHOLD", app->left_hold_button)) { + FURI_LOG_W(TAG, "Could not read LEFTHOLD string"); + furi_string_set(app->left_hold_button, "N/A"); + } else { + if(!infrared_remote_find_button_by_name( + remote, furi_string_get_cstr(app->left_hold_button), &index)) { + FURI_LOG_W(TAG, "Error"); + } else { + left_hold_signal = + infrared_remote_button_get_signal(infrared_remote_get_button(remote, index)); + left_hold_enabled = true; + } + } + + if(!flipper_format_read_string(ff, "RIGHTHOLD", app->right_hold_button)) { + FURI_LOG_W(TAG, "Could not read RIGHTHOLD string"); + furi_string_set(app->right_hold_button, "N/A"); + } else { + if(!infrared_remote_find_button_by_name( + remote, furi_string_get_cstr(app->right_hold_button), &index)) { + FURI_LOG_W(TAG, "Error"); + } else { + right_hold_signal = + infrared_remote_button_get_signal(infrared_remote_get_button(remote, index)); + right_hold_enabled = true; + } + } + + if(!flipper_format_read_string(ff, "OKHOLD", app->ok_hold_button)) { + FURI_LOG_W(TAG, "Could not read OKHOLD string"); + furi_string_set(app->ok_hold_button, "N/A"); + } else { + if(!infrared_remote_find_button_by_name( + remote, furi_string_get_cstr(app->ok_hold_button), &index)) { + FURI_LOG_W(TAG, "Error"); + } else { + ok_hold_signal = + infrared_remote_button_get_signal(infrared_remote_get_button(remote, index)); + ok_hold_enabled = true; + } + } + } + + furi_string_free(remote_path); + + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + bool running = true; + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + if(app->status) { + view_port_update(app->view_port); + while(running) { + if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) { + if(event.type == InputTypeShort) { + switch(event.key) { + case InputKeyBack: + running = false; + break; + default: + break; + } + } + } + } + } else { + view_port_update(app->view_port); + while(running) { + if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) { + // short press signal + if(event.type == InputTypeShort) { + switch(event.key) { + case InputKeyUp: + if(up_enabled) { + infrared_signal_transmit(up_signal); + notification_message(notification, &sequence_blink_start_magenta); + FURI_LOG_I(TAG, "up"); + } + break; + case InputKeyDown: + if(down_enabled) { + infrared_signal_transmit(down_signal); + notification_message(notification, &sequence_blink_start_magenta); + FURI_LOG_I(TAG, "down"); + } + break; + case InputKeyRight: + if(right_enabled) { + infrared_signal_transmit(right_signal); + notification_message(notification, &sequence_blink_start_magenta); + FURI_LOG_I(TAG, "right"); + } + break; + case InputKeyLeft: + if(left_enabled) { + infrared_signal_transmit(left_signal); + notification_message(notification, &sequence_blink_start_magenta); + FURI_LOG_I(TAG, "left"); + } + break; + case InputKeyOk: + if(ok_enabled) { + infrared_signal_transmit(ok_signal); + notification_message(notification, &sequence_blink_start_magenta); + FURI_LOG_I(TAG, "ok"); + } + break; + case InputKeyBack: + if(back_enabled) { + infrared_signal_transmit(back_signal); + notification_message(notification, &sequence_blink_start_magenta); + FURI_LOG_I(TAG, "back"); + } + break; + default: + running = false; + break; + } + // long press signal + } else if(event.type == InputTypeLong) { + switch(event.key) { + case InputKeyUp: + if(up_hold_enabled) { + infrared_signal_transmit(up_hold_signal); + notification_message(notification, &sequence_blink_start_magenta); + FURI_LOG_I(TAG, "up!"); + } + break; + case InputKeyDown: + if(down_hold_enabled) { + infrared_signal_transmit(down_hold_signal); + notification_message(notification, &sequence_blink_start_magenta); + FURI_LOG_I(TAG, "down!"); + } + break; + case InputKeyRight: + if(right_hold_enabled) { + infrared_signal_transmit(right_hold_signal); + notification_message(notification, &sequence_blink_start_magenta); + FURI_LOG_I(TAG, "right!"); + } + break; + case InputKeyLeft: + if(left_hold_enabled) { + infrared_signal_transmit(left_hold_signal); + notification_message(notification, &sequence_blink_start_magenta); + FURI_LOG_I(TAG, "left!"); + } + break; + case InputKeyOk: + if(ok_hold_enabled) { + infrared_signal_transmit(ok_hold_signal); + notification_message(notification, &sequence_blink_start_magenta); + FURI_LOG_I(TAG, "ok!"); + } + break; + default: + running = false; + break; + } + } else if(event.type == InputTypeRelease) { + notification_message(notification, &sequence_blink_stop); + } + } + } + } + + // Free all things + furi_string_free(app->up_button); + furi_string_free(app->down_button); + furi_string_free(app->left_button); + furi_string_free(app->right_button); + furi_string_free(app->ok_button); + furi_string_free(app->back_button); + furi_string_free(app->up_hold_button); + furi_string_free(app->down_hold_button); + furi_string_free(app->left_hold_button); + furi_string_free(app->right_hold_button); + furi_string_free(app->ok_hold_button); + + infrared_remote_free(remote); + view_port_enabled_set(app->view_port, false); + gui_remove_view_port(gui, app->view_port); + view_port_free(app->view_port); + free(app); + furi_message_queue_free(event_queue); + + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote_button.c b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote_button.c new file mode 100644 index 000000000..1f6315ec5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote_button.c @@ -0,0 +1,37 @@ +#include "infrared_remote_button.h" + +#include + +struct InfraredRemoteButton { + FuriString* name; + InfraredSignal* signal; +}; + +InfraredRemoteButton* infrared_remote_button_alloc() { + InfraredRemoteButton* button = malloc(sizeof(InfraredRemoteButton)); + button->name = furi_string_alloc(); + button->signal = infrared_signal_alloc(); + return button; +} + +void infrared_remote_button_free(InfraredRemoteButton* button) { + furi_string_free(button->name); + infrared_signal_free(button->signal); + free(button); +} + +void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name) { + furi_string_set(button->name, name); +} + +const char* infrared_remote_button_get_name(InfraredRemoteButton* button) { + return furi_string_get_cstr(button->name); +} + +void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal) { + infrared_signal_set_signal(button->signal, signal); +} + +InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button) { + return button->signal; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote_button.h b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote_button.h new file mode 100644 index 000000000..f25b759b5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_remote_button.h @@ -0,0 +1,14 @@ +#pragma once + +#include "infrared_signal.h" + +typedef struct InfraredRemoteButton InfraredRemoteButton; + +InfraredRemoteButton* infrared_remote_button_alloc(); +void infrared_remote_button_free(InfraredRemoteButton* button); + +void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name); +const char* infrared_remote_button_get_name(InfraredRemoteButton* button); + +void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal); +InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button); diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_signal.c b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_signal.c new file mode 100644 index 000000000..0c7e3d3bf --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_signal.c @@ -0,0 +1,300 @@ +#include "infrared_signal.h" + +#include +#include +#include +#include +#include + +#define TAG "InfraredSignal" + +struct InfraredSignal { + bool is_raw; + union { + InfraredMessage message; + InfraredRawSignal raw; + } payload; +}; + +static void infrared_signal_clear_timings(InfraredSignal* signal) { + if(signal->is_raw) { + free(signal->payload.raw.timings); + signal->payload.raw.timings_size = 0; + signal->payload.raw.timings = NULL; + } +} + +static bool infrared_signal_is_message_valid(InfraredMessage* message) { + if(!infrared_is_protocol_valid(message->protocol)) { + FURI_LOG_E(TAG, "Unknown protocol"); + return false; + } + + uint32_t address_length = infrared_get_protocol_address_length(message->protocol); + uint32_t address_mask = (1UL << address_length) - 1; + + if(message->address != (message->address & address_mask)) { + FURI_LOG_E( + TAG, + "Address is out of range (mask 0x%08lX): 0x%lX\r\n", + address_mask, + message->address); + return false; + } + + uint32_t command_length = infrared_get_protocol_command_length(message->protocol); + uint32_t command_mask = (1UL << command_length) - 1; + + if(message->command != (message->command & command_mask)) { + FURI_LOG_E( + TAG, + "Command is out of range (mask 0x%08lX): 0x%lX\r\n", + command_mask, + message->command); + return false; + } + + return true; +} + +static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) { + if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) { + FURI_LOG_E( + TAG, + "Frequency is out of range (%X - %X): %lX", + INFRARED_MIN_FREQUENCY, + INFRARED_MAX_FREQUENCY, + raw->frequency); + return false; + + } else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1)) { + FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)raw->duty_cycle); + return false; + + } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) { + FURI_LOG_E( + TAG, + "Timings amount is out of range (0 - %X): %X", + MAX_TIMINGS_AMOUNT, + raw->timings_size); + return false; + } + + return true; +} + +static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) { + const char* protocol_name = infrared_get_protocol_name(message->protocol); + return flipper_format_write_string_cstr(ff, "type", "parsed") && + flipper_format_write_string_cstr(ff, "protocol", protocol_name) && + flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) && + flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4); +} + +static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) { + furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT); + return flipper_format_write_string_cstr(ff, "type", "raw") && + flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) && + flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) && + flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size); +} + +static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) { + FuriString* buf; + buf = furi_string_alloc(); + bool success = false; + + do { + if(!flipper_format_read_string(ff, "protocol", buf)) break; + + InfraredMessage message; + message.protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf)); + + success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) && + flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) && + infrared_signal_is_message_valid(&message); + + if(!success) break; + + infrared_signal_set_message(signal, &message); + } while(0); + + furi_string_free(buf); + return success; +} + +static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) { + uint32_t timings_size, frequency; + float duty_cycle; + + bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) && + flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) && + flipper_format_get_value_count(ff, "data", &timings_size); + + if(!success || timings_size > MAX_TIMINGS_AMOUNT) { + return false; + } + + uint32_t* timings = malloc(sizeof(uint32_t) * timings_size); + success = flipper_format_read_uint32(ff, "data", timings, timings_size); + + if(success) { + infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); + } + + free(timings); + return success; +} + +static bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) { + FuriString* tmp = furi_string_alloc(); + + bool success = false; + + do { + if(!flipper_format_read_string(ff, "type", tmp)) break; + if(furi_string_equal(tmp, "raw")) { + success = infrared_signal_read_raw(signal, ff); + } else if(furi_string_equal(tmp, "parsed")) { + success = infrared_signal_read_message(signal, ff); + } else { + FURI_LOG_E(TAG, "Unknown signal type"); + } + } while(false); + + furi_string_free(tmp); + return success; +} + +InfraredSignal* infrared_signal_alloc() { + InfraredSignal* signal = malloc(sizeof(InfraredSignal)); + + signal->is_raw = false; + signal->payload.message.protocol = InfraredProtocolUnknown; + + return signal; +} + +void infrared_signal_free(InfraredSignal* signal) { + infrared_signal_clear_timings(signal); + free(signal); +} + +bool infrared_signal_is_raw(InfraredSignal* signal) { + return signal->is_raw; +} + +bool infrared_signal_is_valid(InfraredSignal* signal) { + return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) : + infrared_signal_is_message_valid(&signal->payload.message); +} + +void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) { + if(other->is_raw) { + const InfraredRawSignal* raw = &other->payload.raw; + infrared_signal_set_raw_signal( + signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle); + } else { + const InfraredMessage* message = &other->payload.message; + infrared_signal_set_message(signal, message); + } +} + +void infrared_signal_set_raw_signal( + InfraredSignal* signal, + const uint32_t* timings, + size_t timings_size, + uint32_t frequency, + float duty_cycle) { + infrared_signal_clear_timings(signal); + + signal->is_raw = true; + + signal->payload.raw.timings_size = timings_size; + signal->payload.raw.frequency = frequency; + signal->payload.raw.duty_cycle = duty_cycle; + + signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t)); + memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t)); +} + +InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) { + furi_assert(signal->is_raw); + return &signal->payload.raw; +} + +void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) { + infrared_signal_clear_timings(signal); + + signal->is_raw = false; + signal->payload.message = *message; +} + +InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) { + furi_assert(!signal->is_raw); + return &signal->payload.message; +} + +bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) { + if(!flipper_format_write_comment_cstr(ff, "") || + !flipper_format_write_string_cstr(ff, "name", name)) { + return false; + } else if(signal->is_raw) { + return infrared_signal_save_raw(&signal->payload.raw, ff); + } else { + return infrared_signal_save_message(&signal->payload.message, ff); + } +} + +bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) { + FuriString* tmp = furi_string_alloc(); + + bool success = false; + + do { + if(!flipper_format_read_string(ff, "name", tmp)) break; + furi_string_set(name, tmp); + if(!infrared_signal_read_body(signal, ff)) break; + success = true; + } while(0); + + furi_string_free(tmp); + return success; +} + +bool infrared_signal_search_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + const FuriString* name) { + bool success = false; + FuriString* tmp = furi_string_alloc(); + + do { + bool is_name_found = false; + while(flipper_format_read_string(ff, "name", tmp)) { + is_name_found = furi_string_equal(name, tmp); + if(is_name_found) break; + } + if(!is_name_found) break; + if(!infrared_signal_read_body(signal, ff)) break; + success = true; + } while(false); + + furi_string_free(tmp); + return success; +} + +void infrared_signal_transmit(InfraredSignal* signal) { + if(signal->is_raw) { + InfraredRawSignal* raw_signal = &signal->payload.raw; + infrared_send_raw_ext( + raw_signal->timings, + raw_signal->timings_size, + true, + raw_signal->frequency, + raw_signal->duty_cycle); + } else { + InfraredMessage* message = &signal->payload.message; + infrared_send(message, 2); + } +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_signal.h b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_signal.h new file mode 100644 index 000000000..637d859b0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/infrared_signal.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +#include +#include + +typedef struct InfraredSignal InfraredSignal; + +typedef struct { + size_t timings_size; + uint32_t* timings; + uint32_t frequency; + float duty_cycle; +} InfraredRawSignal; + +InfraredSignal* infrared_signal_alloc(); +void infrared_signal_free(InfraredSignal* signal); + +bool infrared_signal_is_raw(InfraredSignal* signal); +bool infrared_signal_is_valid(InfraredSignal* signal); + +void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other); + +void infrared_signal_set_raw_signal( + InfraredSignal* signal, + const uint32_t* timings, + size_t timings_size, + uint32_t frequency, + float duty_cycle); +InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal); + +void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message); +InfraredMessage* infrared_signal_get_message(InfraredSignal* signal); + +bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name); +bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name); +bool infrared_signal_search_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + const FuriString* name); + +void infrared_signal_transmit(InfraredSignal* signal); diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/ir.png b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/ir.png new file mode 100644 index 000000000..71bb60fa3 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/ir.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/ir2.png b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/ir2.png new file mode 100644 index 000000000..133b1c866 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/ir2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ir_remote/ir_10px.png b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/ir_10px.png new file mode 100644 index 000000000..22c986180 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ir_remote/ir_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/mandelbrot/Mandelbrot.png b/Applications/Official/DEV_FW/source/xMasterX/mandelbrot/Mandelbrot.png new file mode 100644 index 000000000..485f70e5c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/mandelbrot/Mandelbrot.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/mandelbrot/application.fam b/Applications/Official/DEV_FW/source/xMasterX/mandelbrot/application.fam new file mode 100644 index 000000000..2a2352f25 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/mandelbrot/application.fam @@ -0,0 +1,12 @@ +App( + appid="MandelbrotSet", + name="Mandelbrot Set", + apptype=FlipperAppType.EXTERNAL, + entry_point="mandelbrot_app", + cdefines=["APP_MANDELBROT_GAME"], + requires=["gui"], + stack_size=1 * 1024, + order=130, + fap_icon="Mandelbrot.png", + fap_category="Games_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/mandelbrot/mandelbrot.c b/Applications/Official/DEV_FW/source/xMasterX/mandelbrot/mandelbrot.c new file mode 100644 index 000000000..bfddc6a97 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/mandelbrot/mandelbrot.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + float xZoom; + float yZoom; + float xOffset; + float yOffset; + float zoom; +} PluginState; + +bool mandelbrot_pixel(int x, int y, float xZoom, float yZoom, float xOffset, float yOffset) { + float ratio = 128.0 / 64.0; + //x0 := scaled x coordinate of pixel (scaled to lie in the Mandelbrot X scale (-2.00, 0.47)) + float x0 = (((x / 128.0) * ratio * xZoom)) - xOffset; + //y0 := scaled y coordinate of pixel (scaled to lie in the Mandelbrot Y scale (-1.12, 1.12)) + float y0 = ((y / 64.0) * yZoom) - yOffset; + float x1 = 0.0; + float y1 = 0.0; + float x2 = 0.0; + float y2 = 0.0; + + int iteration = 0; + int max_iteration = 50; + + while(x2 + y2 <= 4.0 && iteration < max_iteration) { + y1 = 2.0 * x1 * y1 + y0; + x1 = x2 - y2 + x0; + x2 = x1 * x1; + y2 = y1 * y1; + iteration++; + } + + if(iteration > 49) { + return true; + } + + return false; +} + +static void render_callback(Canvas* const canvas, void* ctx) { + const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); + if(plugin_state == NULL) { + return; + } + // border around the edge of the screen + canvas_draw_frame(canvas, 0, 0, 128, 64); + + for(int y = 0; y < 64; y++) { + for(int x = 0; x < 128; x++) { + // did you know if you just pass the indivdiual bits of plugin_state instead of plugin_state + // you dont get any compiler warnings :) + if(mandelbrot_pixel( + x, + y, + plugin_state->xZoom, + plugin_state->yZoom, + plugin_state->xOffset, + plugin_state->yOffset)) { + canvas_draw_dot(canvas, x, y); + } + } + } + + release_mutex((ValueMutex*)ctx, plugin_state); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void mandelbrot_state_init(PluginState* const plugin_state) { + plugin_state->xOffset = 3.0; + plugin_state->yOffset = 1.12; + plugin_state->xZoom = 2.47; + plugin_state->yZoom = 2.24; + plugin_state->zoom = 1; // this controls the camera when +} + +int32_t mandelbrot_app(void* p) { + UNUSED(p); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + PluginState* plugin_state = malloc(sizeof(PluginState)); + mandelbrot_state_init(plugin_state); + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + FURI_LOG_E("mandelbrot", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(plugin_state); + return 255; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + plugin_state->yOffset += 0.1 / plugin_state->zoom; + break; + case InputKeyDown: + plugin_state->yOffset += -0.1 / plugin_state->zoom; + break; + case InputKeyRight: + plugin_state->xOffset += -0.1 / plugin_state->zoom; + break; + case InputKeyLeft: + plugin_state->xOffset += 0.1 / plugin_state->zoom; + break; + case InputKeyOk: + plugin_state->xZoom -= (2.47 / 10) / plugin_state->zoom; + plugin_state->yZoom -= (2.24 / 10) / plugin_state->zoom; + // used to make camera control finer the more zoomed you are + // this needs to be some sort of curve + plugin_state->zoom += 0.15; + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } + } else { + FURI_LOG_D("mandelbrot", "osMessageQueue: event timeout"); + // event timeout + } + view_port_update(view_port); + release_mutex(&state_mutex, plugin_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(event_queue); + + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/montyhall/Monty.png b/Applications/Official/DEV_FW/source/xMasterX/montyhall/Monty.png new file mode 100644 index 000000000..1f4c14cd6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/montyhall/Monty.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/montyhall/application.fam b/Applications/Official/DEV_FW/source/xMasterX/montyhall/application.fam new file mode 100644 index 000000000..c6446b413 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/montyhall/application.fam @@ -0,0 +1,12 @@ +App( + appid="MontyHall", + name="Monty Hall", + apptype=FlipperAppType.EXTERNAL, + entry_point="montyhall_game_app", + cdefines=["APP_MONTYHALL_GAME"], + requires=["gui"], + stack_size=1 * 1024, + order=185, + fap_icon="Monty.png", + fap_category="Games_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/montyhall/monteyhall.c b/Applications/Official/DEV_FW/source/xMasterX/montyhall/monteyhall.c new file mode 100644 index 000000000..024d539ec --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/montyhall/monteyhall.c @@ -0,0 +1,450 @@ +#include +#include +#include +#include + +#include +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 + +//AUTHOR: https://github.com/DevMilanIan +//I_DoorClosed_22x35 sourced from VideoPoker/poker.c -> I_CardBack22x35 +//PRs for syntax, formatting, etc can get you listed as a contributor :) + +// CONCEPT: one of three doors will have a car while the other two have only a goat +// randomize a winning door each round, let the player choose a first selection +// reveal a goat door and allow the player to keep or switch their selection +// based on the Monty Hall problem from Let's Make a Deal + +//void draw_goat(Canvas* canvas, int x, int y) { TODO } + +void draw_car(Canvas* canvas, int x, int y) { + // x -> leftmost pixel, y -> topmost pixel + // could be in another file or a pixel array but idk how to so feel free to PR + + canvas_draw_dot(canvas, x + 1, y + 4); + canvas_draw_dot(canvas, x + 1, y + 5); + canvas_draw_dot(canvas, x + 2, y + 3); + canvas_draw_dot(canvas, x + 2, y + 6); + canvas_draw_dot(canvas, x + 3, y + 3); + canvas_draw_dot(canvas, x + 3, y + 6); + + canvas_draw_dot(canvas, x + 4, y + 2); + canvas_draw_dot(canvas, x + 4, y + 3); + canvas_draw_dot(canvas, x + 4, y + 6); + canvas_draw_dot(canvas, x + 4, y + 7); + + canvas_draw_dot(canvas, x + 5, y + 1); + canvas_draw_dot(canvas, x + 5, y + 2); + canvas_draw_dot(canvas, x + 5, y + 3); + canvas_draw_dot(canvas, x + 5, y + 5); + canvas_draw_dot(canvas, x + 5, y + 8); + + canvas_draw_dot(canvas, x + 6, y); + canvas_draw_dot(canvas, x + 6, y + 1); + canvas_draw_dot(canvas, x + 6, y + 3); + canvas_draw_dot(canvas, x + 6, y + 5); + canvas_draw_dot(canvas, x + 6, y + 8); + + canvas_draw_dot(canvas, x + 7, y); + canvas_draw_dot(canvas, x + 7, y + 3); + canvas_draw_dot(canvas, x + 7, y + 6); + canvas_draw_dot(canvas, x + 7, y + 7); + + canvas_draw_dot(canvas, x + 8, y); + canvas_draw_dot(canvas, x + 8, y + 3); + canvas_draw_dot(canvas, x + 8, y + 6); + + canvas_draw_dot(canvas, x + 9, y); + canvas_draw_dot(canvas, x + 9, y + 3); + canvas_draw_dot(canvas, x + 9, y + 6); + + canvas_draw_dot(canvas, x + 10, y); + canvas_draw_dot(canvas, x + 10, y + 3); + canvas_draw_dot(canvas, x + 10, y + 6); + + canvas_draw_dot(canvas, x + 11, y); + canvas_draw_dot(canvas, x + 11, y + 1); + canvas_draw_dot(canvas, x + 11, y + 3); + canvas_draw_dot(canvas, x + 11, y + 6); + + canvas_draw_dot(canvas, x + 12, y + 1); + canvas_draw_dot(canvas, x + 12, y + 2); + canvas_draw_dot(canvas, x + 12, y + 3); + canvas_draw_dot(canvas, x + 12, y + 6); + canvas_draw_dot(canvas, x + 12, y + 7); + + canvas_draw_dot(canvas, x + 13, y + 2); + canvas_draw_dot(canvas, x + 13, y + 3); + canvas_draw_dot(canvas, x + 13, y + 5); + canvas_draw_dot(canvas, x + 13, y + 8); + + canvas_draw_dot(canvas, x + 14, y + 1); + canvas_draw_dot(canvas, x + 14, y + 2); + canvas_draw_dot(canvas, x + 14, y + 5); + canvas_draw_dot(canvas, x + 14, y + 8); + + canvas_draw_dot(canvas, x + 15, y); + canvas_draw_dot(canvas, x + 15, y + 1); + canvas_draw_dot(canvas, x + 15, y + 6); + canvas_draw_dot(canvas, x + 15, y + 7); + + canvas_draw_dot(canvas, x + 16, y); + canvas_draw_dot(canvas, x + 16, y + 1); + canvas_draw_dot(canvas, x + 16, y + 2); + canvas_draw_dot(canvas, x + 16, y + 3); + canvas_draw_dot(canvas, x + 16, y + 4); + canvas_draw_dot(canvas, x + 16, y + 5); +} + +const uint8_t _I_DoorClosed_22x35_0[] = { + 0x01, 0x00, 0x23, 0x00, 0xfe, 0x7f, 0xe1, 0xf0, 0x28, 0x04, 0x43, 0xe3, 0xff, + 0x91, 0xea, 0x75, 0x52, 0x6a, 0xad, 0x56, 0x5b, 0xad, 0xd5, 0x4a, 0x80, 0xbe, + 0x05, 0xf0, 0x2f, 0x81, 0x7c, 0x0b, 0x45, 0x32, 0x2c, 0x91, 0x7c, 0x8c, 0xa4, +}; +const uint8_t* _I_DoorClosed_22x35[] = {_I_DoorClosed_22x35_0}; +const Icon I_DoorClosed_22x35 = + {.width = 22, .height = 35, .frame_count = 1, .frame_rate = 0, .frames = _I_DoorClosed_22x35}; + +typedef struct { + bool isOpen; + bool isSelected; // picked in RoundOne, RoundThree + bool isWinningDoor; // randomized in RoundOne +} Door; + +typedef struct { + Door doors[3]; + bool didSelect; // false in RoundOne -> RoundTwo when true + bool didSwitch; // determined in RoundFour +} DoorState; + +typedef enum { + RoundOne, // all doors closed, player selects a door when ready + RoundTwo, // door selected, reveal one of the remaining two (can go straight to GameOver) + RoundThree, // player can keep or switch their selection + RoundFour, // reveal all doors + GameOver // score has been updated, allow restart +} GameState; + +typedef struct { + GameState game_state; + DoorState door_state; + uint16_t score; +} MontyState; + +static void montyhall_game_init_state(MontyState* monty_state) { + if(!monty_state->score) { + monty_state->score = 0; + } + monty_state->door_state.didSelect = false; + + for(int i = 0; i < 3; i++) { + monty_state->door_state.doors[i].isOpen = false; + monty_state->door_state.doors[i].isSelected = false; + monty_state->door_state.doors[i].isWinningDoor = false; + } + + monty_state->game_state = RoundOne; + int doorIndex = random() % 3; + monty_state->door_state.doors[doorIndex].isWinningDoor = true; +} + +void selectDoor(MontyState* monty_state, int doorIndex) { + if(monty_state->game_state == RoundOne) { + monty_state->door_state.doors[doorIndex].isSelected = true; + if(monty_state->door_state.doors[doorIndex].isSelected) { + monty_state->door_state.didSelect = true; + monty_state->game_state = RoundTwo; + } + } else if(monty_state->game_state == RoundThree) { + for(int i = 0; i < 3; i++) { + monty_state->door_state.doors[i].isSelected = false; + } + + monty_state->door_state.doors[doorIndex].isSelected = true; + } +} + +int getRandomDoorIndex() { + int randomDoorIndex = random() % 3; + return randomDoorIndex; +} + +void revealBadDoor(MontyState* monty_state) { + int doorToReveal = getRandomDoorIndex(); + while(!monty_state->door_state.doors[doorToReveal].isOpen) { + if(!(monty_state->door_state.doors[doorToReveal].isSelected || + monty_state->door_state.doors[doorToReveal].isWinningDoor)) { + monty_state->door_state.doors[doorToReveal].isOpen = true; + } else { + doorToReveal = getRandomDoorIndex(); + } + } +} + +void revealDoors_updateScore(MontyState* monty_state) { + for(int i = 0; i < 3; i++) { + monty_state->door_state.doors[i].isOpen = true; + + if(monty_state->door_state.doors[i].isWinningDoor && + monty_state->door_state.doors[i].isSelected) { + monty_state->score++; + } + } +} + +static void draw_top(Canvas* canvas, const MontyState* monty_state) { + char buffer[16]; + snprintf(buffer, sizeof(buffer), "Cars: %u", monty_state->score); + canvas_draw_str_aligned(canvas, 2, 8, AlignLeft, AlignBottom, buffer); + + if(monty_state->game_state == RoundThree) { + canvas_draw_str_aligned( + canvas, SCREEN_WIDTH - 5, 8, AlignRight, AlignBottom, "Opened a decoy door"); + } +} + +static void draw_doors(Canvas* canvas, const MontyState* monty_state) { + // {| 16 | <22> | 15 | <22> | 15 | <22> | 16 |} = SCREEN_WIDTH + if(monty_state->door_state.doors[0].isOpen) { + if(monty_state->door_state.doors[0].isWinningDoor) { + canvas_draw_frame(canvas, 16, 12, 22, 35); + draw_car(canvas, 18, 26); + } else { + canvas_draw_frame(canvas, 16, 12, 22, 35); + canvas_draw_str(canvas, 18, 34, "Goat"); + } + } else { + canvas_draw_icon(canvas, 16, 12, &I_DoorClosed_22x35); + } + + if(monty_state->door_state.doors[1].isOpen) { + if(monty_state->door_state.doors[1].isWinningDoor) { + canvas_draw_frame(canvas, 53, 12, 22, 35); + draw_car(canvas, 55, 26); + } else { + canvas_draw_frame(canvas, 53, 12, 22, 35); + canvas_draw_str(canvas, 55, 34, "Goat"); + } + } else { + canvas_draw_icon(canvas, 53, 12, &I_DoorClosed_22x35); + } + + if(monty_state->door_state.doors[2].isOpen) { + if(monty_state->door_state.doors[2].isWinningDoor) { + canvas_draw_frame(canvas, 90, 12, 22, 35); + draw_car(canvas, 92, 26); + } else { + canvas_draw_frame(canvas, 90, 12, 22, 35); + canvas_draw_str(canvas, 92, 34, "Goat"); + } + } else { + canvas_draw_icon(canvas, 90, 12, &I_DoorClosed_22x35); + } +} + +static void draw_bottom(Canvas* canvas, const MontyState* monty_state) { + if(monty_state->game_state == RoundOne) { + elements_button_left(canvas, "Left"); + elements_button_center(canvas, "Center"); + elements_button_right(canvas, "Right"); + } + + if(monty_state->game_state == RoundThree) { + if(monty_state->door_state.doors[0].isSelected) { + elements_button_left(canvas, "Keep"); + if(!monty_state->door_state.doors[1].isOpen) { + elements_button_center(canvas, "Switch"); + } else { + elements_button_right(canvas, "Switch"); + } + } else if(monty_state->door_state.doors[1].isSelected) { + elements_button_center(canvas, "Keep"); + if(!monty_state->door_state.doors[0].isOpen) { + elements_button_left(canvas, "Switch"); + } else { + elements_button_right(canvas, "Switch"); + } + } else if(monty_state->door_state.doors[2].isSelected) { + elements_button_right(canvas, "Keep"); + if(!monty_state->door_state.doors[0].isOpen) { + elements_button_left(canvas, "Switch"); + } else { + elements_button_center(canvas, "Switch"); + } + } + } + + if(monty_state->game_state == RoundFour) { + elements_button_center(canvas, "Reveal"); + } + + if(monty_state->game_state == GameOver) { + canvas_draw_str(canvas, 16, SCREEN_HEIGHT - 5, "Hold center to restart"); + } +} + +static void montyhall_render_callback(Canvas* const canvas, void* ctx) { + const MontyState* monty_state = acquire_mutex((ValueMutex*)ctx, 25); + if(monty_state == NULL) { + return; + } + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + draw_top(canvas, monty_state); + draw_doors(canvas, monty_state); + draw_bottom(canvas, monty_state); + + release_mutex((ValueMutex*)ctx, monty_state); +} + +static void montyhall_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t montyhall_game_app(void* p) { + UNUSED(p); + int32_t return_code = 0; + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + MontyState* monty_state = malloc(sizeof(MontyState)); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, monty_state, sizeof(MontyState))) { + return_code = 255; + goto free_and_exit; + } + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, montyhall_render_callback, &state_mutex); + view_port_input_callback_set(view_port, montyhall_input_callback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + // Start the game + montyhall_game_init_state(monty_state); + + InputEvent event; + for(bool loop = true; loop;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + MontyState* monty_state = (MontyState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + if(event.type == InputTypeShort) { + switch(event.key) { + case InputKeyUp: /* + if(monty_state->game_state == RoundOne) { + monty_state->score++; + } else if(monty_state->game_state == RoundTwo) { + monty_state->score += 2; + } else if(monty_state->game_state == RoundThree) { + monty_state->score += 3; + } else if(monty_state->game_state == RoundFour) { + monty_state->score += 4; + } else if(monty_state->game_state == GameOver) { + monty_state->score += 5; + } */ + break; + case InputKeyDown: /* + if(monty_state->game_state == RoundOne) { + monty_state->score--; + } else if(monty_state->game_state == RoundTwo) { + monty_state->score -= 2; + } else if(monty_state->game_state == RoundThree) { + monty_state->score -= 3; + } else if(monty_state->game_state == RoundFour) { + monty_state->score -= 4; + } else if(monty_state->game_state == GameOver) { + monty_state->score -= 5; + } */ + break; + case InputKeyLeft: + if(monty_state->game_state == RoundOne) { + selectDoor(monty_state, 0); + if(monty_state->game_state == RoundTwo) { + revealBadDoor(monty_state); + monty_state->game_state = RoundThree; + } + } else if(monty_state->game_state == RoundThree) { + if(monty_state->door_state.doors[0].isSelected) { + monty_state->door_state.didSwitch = false; + } else if(!monty_state->door_state.doors[0].isOpen) { + monty_state->door_state.didSwitch = true; + selectDoor(monty_state, 0); + } + monty_state->game_state = RoundFour; + } + break; + case InputKeyOk: + if(monty_state->game_state == RoundOne) { + selectDoor(monty_state, 1); + if(monty_state->game_state == RoundTwo) { + revealBadDoor(monty_state); + monty_state->game_state = RoundThree; + } + } else if(monty_state->game_state == RoundThree) { + if(monty_state->door_state.doors[1].isSelected) { + monty_state->door_state.didSwitch = false; + } else if(!monty_state->door_state.doors[1].isOpen) { + monty_state->door_state.didSwitch = true; + selectDoor(monty_state, 1); + } + monty_state->game_state = RoundFour; + } else if(monty_state->game_state == RoundFour) { + revealDoors_updateScore(monty_state); + monty_state->game_state = GameOver; + } + break; + case InputKeyRight: + if(monty_state->game_state == RoundOne) { + selectDoor(monty_state, 2); + if(monty_state->game_state == RoundTwo) { + revealBadDoor(monty_state); + monty_state->game_state = RoundThree; + } + } else if(monty_state->game_state == RoundThree) { + if(monty_state->door_state.doors[2].isSelected) { + monty_state->door_state.didSwitch = false; + } else if(!monty_state->door_state.doors[2].isOpen) { + monty_state->door_state.didSwitch = true; + selectDoor(monty_state, 2); + } + monty_state->game_state = RoundFour; + } + break; + case InputKeyBack: + loop = false; + break; + default: + break; + } + } + } else if(event.type == InputTypeLong) { + if(event.key == InputKeyOk && monty_state->game_state == GameOver) { + montyhall_game_init_state(monty_state); + } + } + + view_port_update(view_port); + release_mutex(&state_mutex, monty_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + delete_mutex(&state_mutex); + +free_and_exit: + free(monty_state); + furi_message_queue_free(event_queue); + + return return_code; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/music_beeper/application.fam b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/application.fam new file mode 100644 index 000000000..affd2fa4e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/application.fam @@ -0,0 +1,25 @@ +App( + appid="Music_Beeper", + name="Music Beeper", + apptype=FlipperAppType.EXTERNAL, + entry_point="music_beeper_app", + cdefines=["APP_MUSIC_BEEPER"], + requires=[ + "gui", + "dialogs", + ], + provides=["music_beeper_start"], + stack_size=2 * 1024, + order=45, + fap_icon="music_10px.png", + fap_icon_assets="icons", + fap_category="Music_Extra", +) + +App( + appid="music_beeper_start", + apptype=FlipperAppType.STARTUP, + entry_point="music_beeper_on_system_start", + requires=["music_beeper"], + order=30, +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/music_beeper/icons/music_10px.png b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/icons/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/icons/music_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_10px.png b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_beeper.c b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_beeper.c new file mode 100644 index 000000000..edebbc597 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_beeper.c @@ -0,0 +1,367 @@ +#include "music_beeper_worker.h" + +#include +#include + +#include +#include +#include +#include + +#define TAG "MusicBeeper" + +#define MUSIC_BEEPER_APP_PATH_FOLDER ANY_PATH("music_player") +#define MUSIC_BEEPER_APP_EXTENSION "*" + +#define MUSIC_BEEPER_SEMITONE_HISTORY_SIZE 4 + +typedef struct { + uint8_t semitone_history[MUSIC_BEEPER_SEMITONE_HISTORY_SIZE]; + uint8_t duration_history[MUSIC_BEEPER_SEMITONE_HISTORY_SIZE]; + + uint8_t volume; + uint8_t semitone; + uint8_t dots; + uint8_t duration; + float position; +} MusicBeeperModel; + +typedef struct { + MusicBeeperModel* model; + FuriMutex** model_mutex; + + FuriMessageQueue* input_queue; + + ViewPort* view_port; + Gui* gui; + + MusicBeeperWorker* worker; +} MusicBeeper; + +static const float MUSIC_BEEPER_VOLUMES[] = {0, .25, .5, .75, 1}; + +static const char* semitone_to_note(int8_t semitone) { + switch(semitone) { + case 0: + return "C"; + case 1: + return "C#"; + case 2: + return "D"; + case 3: + return "D#"; + case 4: + return "E"; + case 5: + return "F"; + case 6: + return "F#"; + case 7: + return "G"; + case 8: + return "G#"; + case 9: + return "A"; + case 10: + return "A#"; + case 11: + return "B"; + default: + return "--"; + } +} + +static bool is_white_note(uint8_t semitone, uint8_t id) { + switch(semitone) { + case 0: + if(id == 0) return true; + break; + case 2: + if(id == 1) return true; + break; + case 4: + if(id == 2) return true; + break; + case 5: + if(id == 3) return true; + break; + case 7: + if(id == 4) return true; + break; + case 9: + if(id == 5) return true; + break; + case 11: + if(id == 6) return true; + break; + default: + break; + } + + return false; +} + +static bool is_black_note(uint8_t semitone, uint8_t id) { + switch(semitone) { + case 1: + if(id == 0) return true; + break; + case 3: + if(id == 1) return true; + break; + case 6: + if(id == 3) return true; + break; + case 8: + if(id == 4) return true; + break; + case 10: + if(id == 5) return true; + break; + default: + break; + } + + return false; +} + +static void render_callback(Canvas* canvas, void* ctx) { + MusicBeeper* music_beeper = ctx; + furi_check(furi_mutex_acquire(music_beeper->model_mutex, FuriWaitForever) == FuriStatusOk); + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 12, "MusicBeeper"); + + uint8_t x_pos = 0; + uint8_t y_pos = 24; + const uint8_t white_w = 10; + const uint8_t white_h = 40; + + const int8_t black_x = 6; + const int8_t black_y = -5; + const uint8_t black_w = 8; + const uint8_t black_h = 32; + + // white keys + for(size_t i = 0; i < 7; i++) { + if(is_white_note(music_beeper->model->semitone, i)) { + canvas_draw_box(canvas, x_pos + white_w * i, y_pos, white_w + 1, white_h); + } else { + canvas_draw_frame(canvas, x_pos + white_w * i, y_pos, white_w + 1, white_h); + } + } + + // black keys + for(size_t i = 0; i < 7; i++) { + if(i != 2 && i != 6) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box( + canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h); + canvas_set_color(canvas, ColorBlack); + if(is_black_note(music_beeper->model->semitone, i)) { + canvas_draw_box( + canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h); + } else { + canvas_draw_frame( + canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h); + } + } + } + + // volume view_port + x_pos = 124; + y_pos = 0; + const uint8_t volume_h = + (64 / (COUNT_OF(MUSIC_BEEPER_VOLUMES) - 1)) * music_beeper->model->volume; + canvas_draw_frame(canvas, x_pos, y_pos, 4, 64); + canvas_draw_box(canvas, x_pos, y_pos + (64 - volume_h), 4, volume_h); + + // note stack view_port + x_pos = 73; + y_pos = 0; + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_frame(canvas, x_pos, y_pos, 49, 64); + canvas_draw_line(canvas, x_pos + 28, 0, x_pos + 28, 64); + + char duration_text[16]; + for(uint8_t i = 0; i < MUSIC_BEEPER_SEMITONE_HISTORY_SIZE; i++) { + if(music_beeper->model->duration_history[i] == 0xFF) { + snprintf(duration_text, 15, "--"); + } else { + snprintf(duration_text, 15, "%d", music_beeper->model->duration_history[i]); + } + + if(i == 0) { + canvas_draw_box(canvas, x_pos, y_pos + 48, 49, 16); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_color(canvas, ColorBlack); + } + canvas_draw_str( + canvas, + x_pos + 4, + 64 - 16 * i - 3, + semitone_to_note(music_beeper->model->semitone_history[i])); + canvas_draw_str(canvas, x_pos + 31, 64 - 16 * i - 3, duration_text); + canvas_draw_line(canvas, x_pos, 64 - 16 * i, x_pos + 48, 64 - 16 * i); + } + + furi_mutex_release(music_beeper->model_mutex); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + MusicBeeper* music_beeper = ctx; + if(input_event->type == InputTypeShort) { + furi_message_queue_put(music_beeper->input_queue, input_event, 0); + } +} + +static void music_beeper_worker_callback( + uint8_t semitone, + uint8_t dots, + uint8_t duration, + float position, + void* context) { + MusicBeeper* music_beeper = context; + furi_check(furi_mutex_acquire(music_beeper->model_mutex, FuriWaitForever) == FuriStatusOk); + + for(size_t i = 0; i < MUSIC_BEEPER_SEMITONE_HISTORY_SIZE - 1; i++) { + size_t r = MUSIC_BEEPER_SEMITONE_HISTORY_SIZE - 1 - i; + music_beeper->model->duration_history[r] = music_beeper->model->duration_history[r - 1]; + music_beeper->model->semitone_history[r] = music_beeper->model->semitone_history[r - 1]; + } + + semitone = (semitone == 0xFF) ? 0xFF : semitone % 12; + + music_beeper->model->semitone = semitone; + music_beeper->model->dots = dots; + music_beeper->model->duration = duration; + music_beeper->model->position = position; + + music_beeper->model->semitone_history[0] = semitone; + music_beeper->model->duration_history[0] = duration; + + furi_mutex_release(music_beeper->model_mutex); + view_port_update(music_beeper->view_port); +} + +void music_beeper_clear(MusicBeeper* instance) { + memset(instance->model->duration_history, 0xff, MUSIC_BEEPER_SEMITONE_HISTORY_SIZE); + memset(instance->model->semitone_history, 0xff, MUSIC_BEEPER_SEMITONE_HISTORY_SIZE); + music_beeper_worker_clear(instance->worker); +} + +MusicBeeper* music_beeper_alloc() { + MusicBeeper* instance = malloc(sizeof(MusicBeeper)); + + instance->model = malloc(sizeof(MusicBeeperModel)); + instance->model->volume = 4; + + instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + instance->worker = music_beeper_worker_alloc(); + music_beeper_worker_set_volume( + instance->worker, MUSIC_BEEPER_VOLUMES[instance->model->volume]); + music_beeper_worker_set_callback(instance->worker, music_beeper_worker_callback, instance); + + music_beeper_clear(instance); + + instance->view_port = view_port_alloc(); + view_port_draw_callback_set(instance->view_port, render_callback, instance); + view_port_input_callback_set(instance->view_port, input_callback, instance); + + // Open GUI and register view_port + instance->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); + + return instance; +} + +void music_beeper_free(MusicBeeper* instance) { + gui_remove_view_port(instance->gui, instance->view_port); + furi_record_close(RECORD_GUI); + view_port_free(instance->view_port); + + music_beeper_worker_free(instance->worker); + + furi_message_queue_free(instance->input_queue); + + furi_mutex_free(instance->model_mutex); + + free(instance->model); + free(instance); +} + +int32_t music_beeper_app(void* p) { + MusicBeeper* music_beeper = music_beeper_alloc(); + + FuriString* file_path; + file_path = furi_string_alloc(); + + do { + if(p && strlen(p)) { + furi_string_set(file_path, (const char*)p); + } else { + furi_string_set(file_path, MUSIC_BEEPER_APP_PATH_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, MUSIC_BEEPER_APP_EXTENSION, &I_music_10px); + browser_options.hide_ext = false; + + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options); + + furi_record_close(RECORD_DIALOGS); + if(!res) { + FURI_LOG_E(TAG, "No file selected"); + break; + } + } + + if(!music_beeper_worker_load(music_beeper->worker, furi_string_get_cstr(file_path))) { + FURI_LOG_E(TAG, "Unable to load file"); + break; + } + + music_beeper_worker_start(music_beeper->worker); + + InputEvent input; + while(furi_message_queue_get(music_beeper->input_queue, &input, FuriWaitForever) == + FuriStatusOk) { + furi_check( + furi_mutex_acquire(music_beeper->model_mutex, FuriWaitForever) == FuriStatusOk); + + if(input.key == InputKeyBack) { + furi_mutex_release(music_beeper->model_mutex); + break; + } else if(input.key == InputKeyUp) { + if(music_beeper->model->volume < COUNT_OF(MUSIC_BEEPER_VOLUMES) - 1) + music_beeper->model->volume++; + music_beeper_worker_set_volume( + music_beeper->worker, MUSIC_BEEPER_VOLUMES[music_beeper->model->volume]); + } else if(input.key == InputKeyDown) { + if(music_beeper->model->volume > 0) music_beeper->model->volume--; + music_beeper_worker_set_volume( + music_beeper->worker, MUSIC_BEEPER_VOLUMES[music_beeper->model->volume]); + } + + furi_mutex_release(music_beeper->model_mutex); + view_port_update(music_beeper->view_port); + } + + music_beeper_worker_stop(music_beeper->worker); + if(p && strlen(p)) break; // Exit instead of going to browser if launched with arg + music_beeper_clear(music_beeper); + } while(1); + + furi_string_free(file_path); + music_beeper_free(music_beeper); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_beeper_cli.c b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_beeper_cli.c new file mode 100644 index 000000000..26299fa64 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_beeper_cli.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include "music_beeper_worker.h" + +static void music_beeper_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + MusicBeeperWorker* music_beeper_worker = music_beeper_worker_alloc(); + Storage* storage = furi_record_open(RECORD_STORAGE); + + do { + if(storage_common_stat(storage, furi_string_get_cstr(args), NULL) == FSE_OK) { + if(!music_beeper_worker_load(music_beeper_worker, furi_string_get_cstr(args))) { + printf("Failed to open file %s\r\n", furi_string_get_cstr(args)); + break; + } + } else { + if(!music_beeper_worker_load_rtttl_from_string( + music_beeper_worker, furi_string_get_cstr(args))) { + printf("Argument is not a file or RTTTL\r\n"); + break; + } + } + + printf("Press CTRL+C to stop\r\n"); + music_beeper_worker_set_volume(music_beeper_worker, 1.0f); + music_beeper_worker_start(music_beeper_worker); + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_ms(50); + } + music_beeper_worker_stop(music_beeper_worker); + } while(0); + + furi_record_close(RECORD_STORAGE); + music_beeper_worker_free(music_beeper_worker); +} + +void music_beeper_on_system_start() { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + + cli_add_command(cli, "music_beeper", CliCommandFlagDefault, music_beeper_cli, NULL); + + furi_record_close(RECORD_CLI); +#else + UNUSED(music_beeper_cli); +#endif +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_beeper_worker.c b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_beeper_worker.c new file mode 100644 index 000000000..e06e77447 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_beeper_worker.c @@ -0,0 +1,510 @@ +#include "music_beeper_worker.h" + +#include +#include + +#include +#include + +#include + +#define TAG "MusicBeeperWorker" + +#define MUSIC_BEEPER_FILETYPE "Flipper Music Format" +#define MUSIC_BEEPER_VERSION 0 + +#define SEMITONE_PAUSE 0xFF + +#define NOTE_C4 261.63f +#define NOTE_C4_SEMITONE (4.0f * 12.0f) +#define TWO_POW_TWELTH_ROOT 1.059463094359f + +typedef struct { + uint8_t semitone; + uint8_t duration; + uint8_t dots; +} NoteBlock; + +ARRAY_DEF(NoteBlockArray, NoteBlock, M_POD_OPLIST); + +struct MusicBeeperWorker { + FuriThread* thread; + bool should_work; + + MusicBeeperWorkerCallback callback; + void* callback_context; + + float volume; + uint32_t bpm; + uint32_t duration; + uint32_t octave; + NoteBlockArray_t notes; +}; + +static int32_t music_beeper_worker_thread_callback(void* context) { + furi_assert(context); + MusicBeeperWorker* instance = context; + + NoteBlockArray_it_t it; + NoteBlockArray_it(it, instance->notes); + if(furi_hal_speaker_acquire(1000)) { + while(instance->should_work) { + if(NoteBlockArray_end_p(it)) { + NoteBlockArray_it(it, instance->notes); + furi_delay_ms(10); + } else { + NoteBlock* note_block = NoteBlockArray_ref(it); + + float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE; + float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4); + float duration = 60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm / + note_block->duration; + uint32_t dots = note_block->dots; + while(dots > 0) { + duration += duration / 2; + dots--; + } + uint32_t next_tick = furi_get_tick() + duration; + float volume = instance->volume; + + if(instance->callback) { + instance->callback( + note_block->semitone, + note_block->dots, + note_block->duration, + 0.0, + instance->callback_context); + } + + furi_hal_speaker_stop(); + furi_hal_speaker_start(frequency, volume); + while(instance->should_work && furi_get_tick() < next_tick) { + volume *= 1; + furi_hal_speaker_set_volume(volume); + furi_delay_ms(2); + } + NoteBlockArray_next(it); + } + } + + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } else { + FURI_LOG_E(TAG, "Speaker system is busy with another process."); + } + + return 0; +} + +MusicBeeperWorker* music_beeper_worker_alloc() { + MusicBeeperWorker* instance = malloc(sizeof(MusicBeeperWorker)); + + NoteBlockArray_init(instance->notes); + + instance->thread = furi_thread_alloc(); + furi_thread_set_name(instance->thread, "MusicBeeperWorker"); + furi_thread_set_stack_size(instance->thread, 1024); + furi_thread_set_context(instance->thread, instance); + furi_thread_set_callback(instance->thread, music_beeper_worker_thread_callback); + + instance->volume = 1.0f; + + return instance; +} + +void music_beeper_worker_clear(MusicBeeperWorker* instance) { + NoteBlockArray_reset(instance->notes); +} + +void music_beeper_worker_free(MusicBeeperWorker* instance) { + furi_assert(instance); + furi_thread_free(instance->thread); + NoteBlockArray_clear(instance->notes); + free(instance); +} + +static bool is_digit(const char c) { + return isdigit(c) != 0; +} + +static bool is_letter(const char c) { + return islower(c) != 0 || isupper(c) != 0; +} + +static bool is_space(const char c) { + return c == ' ' || c == '\t'; +} + +static size_t extract_number(const char* string, uint32_t* number) { + size_t ret = 0; + *number = 0; + while(is_digit(*string)) { + *number *= 10; + *number += (*string - '0'); + string++; + ret++; + } + return ret; +} + +static size_t extract_dots(const char* string, uint32_t* number) { + size_t ret = 0; + *number = 0; + while(*string == '.') { + *number += 1; + string++; + ret++; + } + return ret; +} + +static size_t extract_char(const char* string, char* symbol) { + if(is_letter(*string)) { + *symbol = *string; + return 1; + } else { + return 0; + } +} + +static size_t extract_sharp(const char* string, char* symbol) { + if(*string == '#' || *string == '_') { + *symbol = '#'; + return 1; + } else { + return 0; + } +} + +static size_t skip_till(const char* string, const char symbol) { + size_t ret = 0; + while(*string != '\0' && *string != symbol) { + string++; + ret++; + } + if(*string != symbol) { + ret = 0; + } + return ret; +} + +static bool music_beeper_worker_add_note( + MusicBeeperWorker* instance, + uint8_t semitone, + uint8_t duration, + uint8_t dots) { + NoteBlock note_block; + + note_block.semitone = semitone; + note_block.duration = duration; + note_block.dots = dots; + + NoteBlockArray_push_back(instance->notes, note_block); + + return true; +} + +static int8_t note_to_semitone(const char note) { + switch(note) { + case 'C': + return 0; + // C# + case 'D': + return 2; + // D# + case 'E': + return 4; + case 'F': + return 5; + // F# + case 'G': + return 7; + // G# + case 'A': + return 9; + // A# + case 'B': + return 11; + default: + return 0; + } +} + +static bool music_beeper_worker_parse_notes(MusicBeeperWorker* instance, const char* string) { + const char* cursor = string; + bool result = true; + + while(*cursor != '\0') { + if(!is_space(*cursor)) { + uint32_t duration = 0; + char note_char = '\0'; + char sharp_char = '\0'; + uint32_t octave = 0; + uint32_t dots = 0; + + // Parsing + cursor += extract_number(cursor, &duration); + cursor += extract_char(cursor, ¬e_char); + cursor += extract_sharp(cursor, &sharp_char); + cursor += extract_number(cursor, &octave); + cursor += extract_dots(cursor, &dots); + + // Post processing + note_char = toupper(note_char); + if(!duration) { + duration = instance->duration; + } + if(!octave) { + octave = instance->octave; + } + + // Validation + bool is_valid = true; + is_valid &= (duration >= 1 && duration <= 128); + is_valid &= ((note_char >= 'A' && note_char <= 'G') || note_char == 'P'); + is_valid &= (sharp_char == '#' || sharp_char == '\0'); + is_valid &= (octave <= 16); + is_valid &= (dots <= 16); + if(!is_valid) { + FURI_LOG_E( + TAG, + "Invalid note: %lu%c%c%lu.%lu", + duration, + note_char == '\0' ? '_' : note_char, + sharp_char == '\0' ? '_' : sharp_char, + octave, + dots); + result = false; + break; + } + + // Note to semitones + uint8_t semitone = 0; + if(note_char == 'P') { + semitone = SEMITONE_PAUSE; + } else { + semitone += octave * 12; + semitone += note_to_semitone(note_char); + semitone += sharp_char == '#' ? 1 : 0; + } + + if(music_beeper_worker_add_note(instance, semitone, duration, dots)) { + FURI_LOG_D( + TAG, + "Added note: %c%c%lu.%lu = %u %lu", + note_char == '\0' ? '_' : note_char, + sharp_char == '\0' ? '_' : sharp_char, + octave, + dots, + semitone, + duration); + } else { + FURI_LOG_E( + TAG, + "Invalid note: %c%c%lu.%lu = %u %lu", + note_char == '\0' ? '_' : note_char, + sharp_char == '\0' ? '_' : sharp_char, + octave, + dots, + semitone, + duration); + } + cursor += skip_till(cursor, ','); + } + + if(*cursor != '\0') cursor++; + } + + return result; +} + +bool music_beeper_worker_load(MusicBeeperWorker* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + bool ret = false; + if(strcasestr(file_path, ".fmf")) { + ret = music_beeper_worker_load_fmf_from_file(instance, file_path); + } else { + ret = music_beeper_worker_load_rtttl_from_file(instance, file_path); + } + return ret; +} + +bool music_beeper_worker_load_fmf_from_file(MusicBeeperWorker* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + bool result = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + do { + if(!flipper_format_file_open_existing(file, file_path)) break; + + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + if(furi_string_cmp_str(temp_str, MUSIC_BEEPER_FILETYPE) || + (version != MUSIC_BEEPER_VERSION)) { + FURI_LOG_E(TAG, "Incorrect file format or version"); + break; + } + + if(!flipper_format_read_uint32(file, "BPM", &instance->bpm, 1)) { + FURI_LOG_E(TAG, "BPM is missing"); + break; + } + if(!flipper_format_read_uint32(file, "Duration", &instance->duration, 1)) { + FURI_LOG_E(TAG, "Duration is missing"); + break; + } + if(!flipper_format_read_uint32(file, "Octave", &instance->octave, 1)) { + FURI_LOG_E(TAG, "Octave is missing"); + break; + } + + if(!flipper_format_read_string(file, "Notes", temp_str)) { + FURI_LOG_E(TAG, "Notes is missing"); + break; + } + + if(!music_beeper_worker_parse_notes(instance, furi_string_get_cstr(temp_str))) { + break; + } + + result = true; + } while(false); + + furi_record_close(RECORD_STORAGE); + flipper_format_free(file); + furi_string_free(temp_str); + + return result; +} + +bool music_beeper_worker_load_rtttl_from_file(MusicBeeperWorker* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + bool result = false; + FuriString* content; + content = furi_string_alloc(); + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + do { + if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Unable to open file"); + break; + }; + + uint16_t ret = 0; + do { + uint8_t buffer[65] = {0}; + ret = storage_file_read(file, buffer, sizeof(buffer) - 1); + for(size_t i = 0; i < ret; i++) { + furi_string_push_back(content, buffer[i]); + } + } while(ret > 0); + + furi_string_trim(content); + if(!furi_string_size(content)) { + FURI_LOG_E(TAG, "Empty file"); + break; + } + + if(!music_beeper_worker_load_rtttl_from_string(instance, furi_string_get_cstr(content))) { + FURI_LOG_E(TAG, "Invalid file content"); + break; + } + + result = true; + } while(0); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + furi_string_free(content); + + return result; +} + +bool music_beeper_worker_load_rtttl_from_string(MusicBeeperWorker* instance, const char* string) { + furi_assert(instance); + + const char* cursor = string; + + // Skip name + cursor += skip_till(cursor, ':'); + if(*cursor != ':') { + return false; + } + + // Duration + cursor += skip_till(cursor, '='); + if(*cursor != '=') { + return false; + } + cursor++; + cursor += extract_number(cursor, &instance->duration); + + // Octave + cursor += skip_till(cursor, '='); + if(*cursor != '=') { + return false; + } + cursor++; + cursor += extract_number(cursor, &instance->octave); + + // BPM + cursor += skip_till(cursor, '='); + if(*cursor != '=') { + return false; + } + cursor++; + cursor += extract_number(cursor, &instance->bpm); + + // Notes + cursor += skip_till(cursor, ':'); + if(*cursor != ':') { + return false; + } + cursor++; + if(!music_beeper_worker_parse_notes(instance, cursor)) { + return false; + } + + return true; +} + +void music_beeper_worker_set_callback( + MusicBeeperWorker* instance, + MusicBeeperWorkerCallback callback, + void* context) { + furi_assert(instance); + instance->callback = callback; + instance->callback_context = context; +} + +void music_beeper_worker_set_volume(MusicBeeperWorker* instance, float volume) { + furi_assert(instance); + instance->volume = volume; +} + +void music_beeper_worker_start(MusicBeeperWorker* instance) { + furi_assert(instance); + furi_assert(instance->should_work == false); + + instance->should_work = true; + furi_thread_start(instance->thread); +} + +void music_beeper_worker_stop(MusicBeeperWorker* instance) { + furi_assert(instance); + furi_assert(instance->should_work == true); + + instance->should_work = false; + furi_thread_join(instance->thread); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_beeper_worker.h b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_beeper_worker.h new file mode 100644 index 000000000..bc30abf81 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/music_beeper/music_beeper_worker.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*MusicBeeperWorkerCallback)( + uint8_t semitone, + uint8_t dots, + uint8_t duration, + float position, + void* context); + +typedef struct MusicBeeperWorker MusicBeeperWorker; + +MusicBeeperWorker* music_beeper_worker_alloc(); + +void music_beeper_worker_clear(MusicBeeperWorker* instance); + +void music_beeper_worker_free(MusicBeeperWorker* instance); + +bool music_beeper_worker_load(MusicBeeperWorker* instance, const char* file_path); + +bool music_beeper_worker_load_fmf_from_file(MusicBeeperWorker* instance, const char* file_path); + +bool music_beeper_worker_load_rtttl_from_file(MusicBeeperWorker* instance, const char* file_path); + +bool music_beeper_worker_load_rtttl_from_string(MusicBeeperWorker* instance, const char* string); + +void music_beeper_worker_set_callback( + MusicBeeperWorker* instance, + MusicBeeperWorkerCallback callback, + void* context); + +void music_beeper_worker_set_volume(MusicBeeperWorker* instance, float volume); + +void music_beeper_worker_start(MusicBeeperWorker* instance); + +void music_beeper_worker_stop(MusicBeeperWorker* instance); + +#ifdef __cplusplus +} +#endif diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/.github/workflows/build_dev.yml b/Applications/Official/DEV_FW/source/xMasterX/musictracker/.github/workflows/build_dev.yml new file mode 100644 index 000000000..4d3da2331 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/musictracker/.github/workflows/build_dev.yml @@ -0,0 +1,19 @@ +name: Build dev + +on: + push: + branches: + - master + +jobs: + build_dev: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Build + uses: oleksiikutuzov/flipperzero-ufbt-action@v1 + with: + channel: dev \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/README.md b/Applications/Official/DEV_FW/source/xMasterX/musictracker/README.md new file mode 100644 index 000000000..584c2ff86 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/musictracker/README.md @@ -0,0 +1,4 @@ +# Flipper Zero music tracker +-=-=- MVP Stage: minimum viable player -=-=- + +[>Get latest build<](https://nightly.link/DrZlo13/flipper-zero-music-tracker/workflows/build_dev/master/zero_tracker.fap.zip) diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/application.fam b/Applications/Official/DEV_FW/source/xMasterX/musictracker/application.fam new file mode 100644 index 000000000..15328c3fa --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/musictracker/application.fam @@ -0,0 +1,14 @@ +App( + appid="zero_tracker", + name="Zero Tracker", + apptype=FlipperAppType.PLUGIN, + entry_point="zero_tracker_app", + requires=[ + "gui", + ], + stack_size=4 * 1024, + order=20, + fap_icon="zero_tracker.png", + fap_category="Music_Extra", + fap_icon_assets="icons", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/icons/.gitignore b/Applications/Official/DEV_FW/source/xMasterX/musictracker/icons/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/speaker_hal.c b/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/speaker_hal.c new file mode 100644 index 000000000..94489f1b6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/speaker_hal.c @@ -0,0 +1,107 @@ +#include "speaker_hal.h" + +#define FURI_HAL_SPEAKER_TIMER TIM16 +#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 +#define FURI_HAL_SPEAKER_PRESCALER 500 + +void tracker_speaker_play(float frequency, float pwm) { + uint32_t autoreload = (SystemCoreClock / FURI_HAL_SPEAKER_PRESCALER / frequency) - 1; + if(autoreload < 2) { + autoreload = 2; + } else if(autoreload > UINT16_MAX) { + autoreload = UINT16_MAX; + } + + if(pwm < 0) pwm = 0; + if(pwm > 1) pwm = 1; + + uint32_t compare_value = pwm * autoreload; + + if(compare_value == 0) { + compare_value = 1; + } + + if(LL_TIM_OC_GetCompareCH1(FURI_HAL_SPEAKER_TIMER) != compare_value) { + LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, compare_value); + } + + if(LL_TIM_GetAutoReload(FURI_HAL_SPEAKER_TIMER) != autoreload) { + LL_TIM_SetAutoReload(FURI_HAL_SPEAKER_TIMER, autoreload); + if(LL_TIM_GetCounter(FURI_HAL_SPEAKER_TIMER) > autoreload) { + LL_TIM_SetCounter(FURI_HAL_SPEAKER_TIMER, 0); + } + } + + LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); +} + +void tracker_speaker_stop() { + LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); +} + +void tracker_speaker_init() { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + furi_hal_speaker_start(200.0f, 0.01f); + tracker_speaker_stop(); + } +} + +void tracker_speaker_deinit() { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } +} + +static FuriHalInterruptISR tracker_isr; +static void* tracker_isr_context; +static void tracker_interrupt_cb(void* context) { + UNUSED(context); + + if(LL_TIM_IsActiveFlag_UPDATE(TIM2)) { + LL_TIM_ClearFlag_UPDATE(TIM2); + + if(tracker_isr) { + tracker_isr(tracker_isr_context); + } + } +} + +void tracker_interrupt_init(float freq, FuriHalInterruptISR isr, void* context) { + tracker_isr = isr; + tracker_isr_context = context; + + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, tracker_interrupt_cb, NULL); + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + // Prescaler to get 1kHz clock + TIM_InitStruct.Prescaler = SystemCoreClock / 1000000 - 1; + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + // Auto reload to get freq Hz interrupt + TIM_InitStruct.Autoreload = (1000000 / freq) - 1; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(TIM2, &TIM_InitStruct); + LL_TIM_EnableIT_UPDATE(TIM2); + LL_TIM_EnableAllOutputs(TIM2); + LL_TIM_EnableCounter(TIM2); +} + +void tracker_interrupt_deinit() { + FURI_CRITICAL_ENTER(); + LL_TIM_DeInit(TIM2); + FURI_CRITICAL_EXIT(); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); +} + +void tracker_debug_init() { + furi_hal_gpio_init(&gpio_ext_pc3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); +} + +void tracker_debug_set(bool value) { + furi_hal_gpio_write(&gpio_ext_pc3, value); +} + +void tracker_debug_deinit() { + furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/speaker_hal.h b/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/speaker_hal.h new file mode 100644 index 000000000..7867fe93f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/speaker_hal.h @@ -0,0 +1,19 @@ +#include + +void tracker_speaker_init(); + +void tracker_speaker_deinit(); + +void tracker_speaker_play(float frequency, float pwm); + +void tracker_speaker_stop(); + +void tracker_interrupt_init(float freq, FuriHalInterruptISR isr, void* context); + +void tracker_interrupt_deinit(); + +void tracker_debug_init(); + +void tracker_debug_set(bool value); + +void tracker_debug_deinit(); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/tracker.c b/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/tracker.c new file mode 100644 index 000000000..e5efcea17 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/tracker.c @@ -0,0 +1,441 @@ +#include "tracker.h" +#include +#include "speaker_hal.h" + +// SongState song_state = { +// .tick = 0, +// .tick_limit = 2, +// .row = 0, +// }; + +typedef struct { + uint8_t speed; + uint8_t depth; + int8_t direction; + int8_t value; +} IntegerOscillator; + +typedef struct { + float frequency; + float frequency_target; + float pwm; + bool play; + IntegerOscillator vibrato; +} ChannelState; + +typedef struct { + ChannelState* channels; + uint8_t tick; + uint8_t tick_limit; + + uint8_t pattern_index; + uint8_t row_index; + uint8_t order_list_index; +} SongState; + +typedef struct { + uint8_t note; + uint8_t effect; + uint8_t data; +} UnpackedRow; + +struct Tracker { + const Song* song; + bool playing; + TrackerMessageCallback callback; + void* context; + SongState song_state; +}; + +static void channels_state_init(ChannelState* channel) { + channel->frequency = 0; + channel->frequency_target = FREQUENCY_UNSET; + channel->pwm = PWM_DEFAULT; + channel->play = false; + channel->vibrato.speed = 0; + channel->vibrato.depth = 0; + channel->vibrato.direction = 0; + channel->vibrato.value = 0; +} + +static void tracker_song_state_init(Tracker* tracker) { + tracker->song_state.tick = 0; + tracker->song_state.tick_limit = 2; + tracker->song_state.row_index = 0; + tracker->song_state.order_list_index = 0; + tracker->song_state.pattern_index = tracker->song->order_list[0]; + + if(tracker->song_state.channels != NULL) { + free(tracker->song_state.channels); + } + + tracker->song_state.channels = malloc(sizeof(ChannelState) * tracker->song->channels_count); + for(uint8_t i = 0; i < tracker->song->channels_count; i++) { + channels_state_init(&tracker->song_state.channels[i]); + } +} + +static void tracker_song_state_clear(Tracker* tracker) { + if(tracker->song_state.channels != NULL) { + free(tracker->song_state.channels); + tracker->song_state.channels = NULL; + } +} + +static uint8_t record_get_note(Row note) { + return note & ROW_NOTE_MASK; +} + +static uint8_t record_get_effect(Row note) { + return (note >> 6) & ROW_EFFECT_MASK; +} + +static uint8_t record_get_effect_data(Row note) { + return (note >> 10) & ROW_EFFECT_DATA_MASK; +} + +#define NOTES_PER_OCT 12 +const float notes_oct[NOTES_PER_OCT] = { + 130.813f, + 138.591f, + 146.832f, + 155.563f, + 164.814f, + 174.614f, + 184.997f, + 195.998f, + 207.652f, + 220.00f, + 233.082f, + 246.942f, +}; + +static float note_to_freq(uint8_t note) { + if(note == NOTE_NONE) return 0.0f; + note = note - NOTE_C2; + uint8_t octave = note / NOTES_PER_OCT; + uint8_t note_in_oct = note % NOTES_PER_OCT; + return notes_oct[note_in_oct] * (1 << octave); +} + +static float frequency_offset_semitones(float frequency, uint8_t semitones) { + return frequency * (1.0f + ((1.0f / 12.0f) * semitones)); +} + +static float frequency_get_seventh_of_a_semitone(float frequency) { + return frequency * ((1.0f / 12.0f) / 7.0f); +} + +static UnpackedRow get_current_row(const Song* song, SongState* song_state, uint8_t channel) { + const Pattern* pattern = &song->patterns[song_state->pattern_index]; + const Row row = pattern->channels[channel].rows[song_state->row_index]; + return (UnpackedRow){ + .note = record_get_note(row), + .effect = record_get_effect(row), + .data = record_get_effect_data(row), + }; +} + +static int16_t advance_order_and_get_next_pattern_index(const Song* song, SongState* song_state) { + song_state->order_list_index++; + if(song_state->order_list_index >= song->order_list_size) { + return -1; + } else { + return song->order_list[song_state->order_list_index]; + } +} + +typedef struct { + int16_t pattern; + int16_t row; + bool change_pattern; + bool change_row; +} Location; + +static void tracker_send_position_message(Tracker* tracker) { + if(tracker->callback != NULL) { + tracker->callback( + (TrackerMessage){ + .type = TrackerPositionChanged, + .data = + { + .position = + { + .order_list_index = tracker->song_state.order_list_index, + .row = tracker->song_state.row_index, + }, + }, + }, + tracker->context); + } +} + +static void tracker_send_end_message(Tracker* tracker) { + if(tracker->callback != NULL) { + tracker->callback((TrackerMessage){.type = TrackerEndOfSong}, tracker->context); + } +} + +static void advance_to_pattern(Tracker* tracker, Location advance) { + if(advance.change_pattern) { + if(advance.pattern < 0 || advance.pattern >= tracker->song->patterns_count) { + tracker->playing = false; + tracker_send_end_message(tracker); + } else { + tracker->song_state.pattern_index = advance.pattern; + tracker->song_state.row_index = 0; + } + } + + if(advance.change_row) { + if(advance.row < 0) advance.row = 0; + if(advance.row >= PATTERN_SIZE) advance.row = PATTERN_SIZE - 1; + tracker->song_state.row_index = advance.row; + } + + tracker_send_position_message(tracker); +} + +static void tracker_interrupt_body(Tracker* tracker) { + if(!tracker->playing) { + tracker_speaker_stop(); + return; + } + + const uint8_t channel_index = 0; + SongState* song_state = &tracker->song_state; + ChannelState* channel_state = &song_state->channels[channel_index]; + const Song* song = tracker->song; + UnpackedRow row = get_current_row(song, song_state, channel_index); + + // load frequency from note at tick 0 + if(song_state->tick == 0) { + bool invalidate_row = false; + // handle "on first tick" effects + if(row.effect == EffectBreakPattern) { + int16_t next_row_index = row.data; + int16_t next_pattern_index = + advance_order_and_get_next_pattern_index(song, song_state); + advance_to_pattern( + tracker, + (Location){ + .pattern = next_pattern_index, + .row = next_row_index, + .change_pattern = true, + .change_row = true, + }); + + invalidate_row = true; + } + + if(row.effect == EffectJumpToOrder) { + song_state->order_list_index = row.data; + int16_t next_pattern_index = song->order_list[song_state->order_list_index]; + + advance_to_pattern( + tracker, + (Location){ + .pattern = next_pattern_index, + .change_pattern = true, + }); + + invalidate_row = true; + } + + // tracker state can be affected by effects + if(!tracker->playing) { + tracker_speaker_stop(); + return; + } + + if(invalidate_row) { + row = get_current_row(song, song_state, channel_index); + + if(row.effect == EffectSetSpeed) { + song_state->tick_limit = row.data; + } + } + + // handle note effects + if(row.note == NOTE_OFF) { + channel_state->play = false; + } else if((row.note > NOTE_NONE) && (row.note < NOTE_OFF)) { + channel_state->play = true; + + // reset vibrato + channel_state->vibrato.speed = 0; + channel_state->vibrato.depth = 0; + channel_state->vibrato.value = 0; + channel_state->vibrato.direction = 0; + + // reset pwm + channel_state->pwm = PWM_DEFAULT; + + if(row.effect == EffectSlideToNote) { + channel_state->frequency_target = note_to_freq(row.note); + } else { + channel_state->frequency = note_to_freq(row.note); + channel_state->frequency_target = FREQUENCY_UNSET; + } + } + } + + if(channel_state->play) { + float frequency, pwm; + + if((row.effect == EffectSlideUp || row.effect == EffectSlideDown) && + row.data != EFFECT_DATA_NONE) { + // apply slide effect + channel_state->frequency += (row.effect == EffectSlideUp ? 1 : -1) * row.data; + } else if(row.effect == EffectSlideToNote) { + // apply slide to note effect, if target frequency is set + if(channel_state->frequency_target > 0) { + if(channel_state->frequency_target > channel_state->frequency) { + channel_state->frequency += row.data; + if(channel_state->frequency > channel_state->frequency_target) { + channel_state->frequency = channel_state->frequency_target; + channel_state->frequency_target = FREQUENCY_UNSET; + } + } else if(channel_state->frequency_target < channel_state->frequency) { + channel_state->frequency -= row.data; + if(channel_state->frequency < channel_state->frequency_target) { + channel_state->frequency = channel_state->frequency_target; + channel_state->frequency_target = FREQUENCY_UNSET; + } + } + } + } + + frequency = channel_state->frequency; + pwm = channel_state->pwm; + + // apply arpeggio effect + if(row.effect == EffectArpeggio) { + if(row.data != EFFECT_DATA_NONE) { + if((song_state->tick % 3) == 1) { + uint8_t note_offset = EFFECT_DATA_GET_X(row.data); + frequency = frequency_offset_semitones(frequency, note_offset); + } else if((song_state->tick % 3) == 2) { + uint8_t note_offset = EFFECT_DATA_GET_Y(row.data); + frequency = frequency_offset_semitones(frequency, note_offset); + } + } + } else if(row.effect == EffectVibrato) { + // apply vibrato effect, data = speed, depth + uint8_t vibrato_speed = EFFECT_DATA_GET_X(row.data); + uint8_t vibrato_depth = EFFECT_DATA_GET_Y(row.data); + + // update vibrato parameters if speed or depth is non-zero + if(vibrato_speed != 0) channel_state->vibrato.speed = vibrato_speed; + if(vibrato_depth != 0) channel_state->vibrato.depth = vibrato_depth; + + // update vibrato value + channel_state->vibrato.value += + channel_state->vibrato.direction * channel_state->vibrato.speed; + + // change direction if value is at the limit + if(channel_state->vibrato.value > channel_state->vibrato.depth) { + channel_state->vibrato.direction = -1; + } else if(channel_state->vibrato.value < -channel_state->vibrato.depth) { + channel_state->vibrato.direction = 1; + } else if(channel_state->vibrato.direction == 0) { + // set initial direction, if it is not set + channel_state->vibrato.direction = 1; + } + + frequency += + (frequency_get_seventh_of_a_semitone(frequency) * channel_state->vibrato.value); + } else if(row.effect == EffectPWM) { + pwm = (pwm - PWM_MIN) / EFFECT_DATA_1_MAX * row.data + PWM_MIN; + } + + tracker_speaker_play(frequency, pwm); + } else { + tracker_speaker_stop(); + } + + song_state->tick++; + if(song_state->tick >= song_state->tick_limit) { + song_state->tick = 0; + + // next note + song_state->row_index = (song_state->row_index + 1); + + if(song_state->row_index >= PATTERN_SIZE) { + int16_t next_pattern_index = + advance_order_and_get_next_pattern_index(song, song_state); + advance_to_pattern( + tracker, + (Location){ + .pattern = next_pattern_index, + .change_pattern = true, + }); + } else { + tracker_send_position_message(tracker); + } + } +} + +static void tracker_interrupt_cb(void* context) { + Tracker* tracker = (Tracker*)context; + tracker_debug_set(true); + tracker_interrupt_body(tracker); + tracker_debug_set(false); +} + +/********************************************************************* + * Tracker Interface + *********************************************************************/ + +Tracker* tracker_alloc() { + Tracker* tracker = malloc(sizeof(Tracker)); + return tracker; +} + +void tracker_free(Tracker* tracker) { + tracker_song_state_clear(tracker); + free(tracker); +} + +void tracker_set_message_callback(Tracker* tracker, TrackerMessageCallback callback, void* context) { + furi_check(tracker->playing == false); + tracker->callback = callback; + tracker->context = context; +} + +void tracker_set_song(Tracker* tracker, const Song* song) { + furi_check(tracker->playing == false); + tracker->song = song; + tracker_song_state_init(tracker); +} + +void tracker_set_order_index(Tracker* tracker, uint8_t order_index) { + furi_check(tracker->playing == false); + furi_check(order_index < tracker->song->order_list_size); + tracker->song_state.order_list_index = order_index; + tracker->song_state.pattern_index = tracker->song->order_list[order_index]; +} + +void tracker_set_row(Tracker* tracker, uint8_t row) { + furi_check(tracker->playing == false); + furi_check(row < PATTERN_SIZE); + tracker->song_state.row_index = row; +} + +void tracker_start(Tracker* tracker) { + furi_check(tracker->song != NULL); + + tracker->playing = true; + tracker_send_position_message(tracker); + tracker_debug_init(); + tracker_speaker_init(); + tracker_interrupt_init(tracker->song->ticks_per_second, tracker_interrupt_cb, tracker); +} + +void tracker_stop(Tracker* tracker) { + tracker_interrupt_deinit(); + tracker_speaker_deinit(); + tracker_debug_deinit(); + + tracker->playing = false; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/tracker.h b/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/tracker.h new file mode 100644 index 000000000..70bf4bd6b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/tracker.h @@ -0,0 +1,38 @@ +#pragma once +#include "tracker_notes.h" +#include "tracker_song.h" + +typedef enum { + TrackerPositionChanged, + TrackerEndOfSong, +} TrackerMessageType; + +typedef struct { + TrackerMessageType type; + union tracker_message_data { + struct { + uint8_t order_list_index; + uint8_t row; + } position; + } data; +} TrackerMessage; + +typedef void (*TrackerMessageCallback)(TrackerMessage message, void* context); + +typedef struct Tracker Tracker; + +Tracker* tracker_alloc(); + +void tracker_free(Tracker* tracker); + +void tracker_set_message_callback(Tracker* tracker, TrackerMessageCallback callback, void* context); + +void tracker_set_song(Tracker* tracker, const Song* song); + +void tracker_set_order_index(Tracker* tracker, uint8_t order_index); + +void tracker_set_row(Tracker* tracker, uint8_t row); + +void tracker_start(Tracker* tracker); + +void tracker_stop(Tracker* tracker); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/tracker_notes.h b/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/tracker_notes.h new file mode 100644 index 000000000..22ab3590f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/tracker_notes.h @@ -0,0 +1,64 @@ +#pragma once + +#define NOTE_NONE 0 +#define NOTE_C2 1 +#define NOTE_Cs2 2 +#define NOTE_D2 3 +#define NOTE_Ds2 4 +#define NOTE_E2 5 +#define NOTE_F2 6 +#define NOTE_Fs2 7 +#define NOTE_G2 8 +#define NOTE_Gs2 9 +#define NOTE_A2 10 +#define NOTE_As2 11 +#define NOTE_B2 12 +#define NOTE_C3 13 +#define NOTE_Cs3 14 +#define NOTE_D3 15 +#define NOTE_Ds3 16 +#define NOTE_E3 17 +#define NOTE_F3 18 +#define NOTE_Fs3 19 +#define NOTE_G3 20 +#define NOTE_Gs3 21 +#define NOTE_A3 22 +#define NOTE_As3 23 +#define NOTE_B3 24 +#define NOTE_C4 25 +#define NOTE_Cs4 26 +#define NOTE_D4 27 +#define NOTE_Ds4 28 +#define NOTE_E4 29 +#define NOTE_F4 30 +#define NOTE_Fs4 31 +#define NOTE_G4 32 +#define NOTE_Gs4 33 +#define NOTE_A4 34 +#define NOTE_As4 35 +#define NOTE_B4 36 +#define NOTE_C5 37 +#define NOTE_Cs5 38 +#define NOTE_D5 39 +#define NOTE_Ds5 40 +#define NOTE_E5 41 +#define NOTE_F5 42 +#define NOTE_Fs5 43 +#define NOTE_G5 44 +#define NOTE_Gs5 45 +#define NOTE_A5 46 +#define NOTE_As5 47 +#define NOTE_B5 48 +#define NOTE_C6 49 +#define NOTE_Cs6 50 +#define NOTE_D6 51 +#define NOTE_Ds6 52 +#define NOTE_E6 53 +#define NOTE_F6 54 +#define NOTE_Fs6 55 +#define NOTE_G6 56 +#define NOTE_Gs6 57 +#define NOTE_A6 58 +#define NOTE_As6 59 +#define NOTE_B6 60 +#define NOTE_OFF 63 \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/tracker_song.h b/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/tracker_song.h new file mode 100644 index 000000000..7a054f7b1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/musictracker/tracker_engine/tracker_song.h @@ -0,0 +1,109 @@ +#pragma once +#include + +/** + * @brief Row + * + * AH AL + * FEDCBA98 76543210 + * nnnnnnee eedddddd + * -------- -------- + * nnnnnn = [0] do nothing, [1..60] note number, [61] note off, [62..63] not used + * ee ee = [0..F] effect + * 111222 = [0..63] or [0..7, 0..7] effect data + */ +typedef uint16_t Row; + +#define ROW_NOTE_MASK 0x3F +#define ROW_EFFECT_MASK 0x0F +#define ROW_EFFECT_DATA_MASK 0x3F + +typedef enum { + // 0xy, x - first semitones offset, y - second semitones offset. 0 - no offset .. 7 - +7 semitones... + // Play the arpeggio chord with three notes. The first note is the base note, the second and third are offset by x and y. + // Each note plays one tick. + EffectArpeggio = 0x00, + + // 1xx, xx - effect speed, 0 - no effect, 1 - slowest, 0x3F - fastest. + // Slide the note pitch up by xx Hz every tick. + EffectSlideUp = 0x01, + + // 2xx, xx - effect speed, 0 - no effect, 1 - slowest, 0x3F - fastest. + // Slide the note pitch down by xx Hz every tick. + EffectSlideDown = 0x02, + + // 3xx, xx - effect speed, 0 - no effect, 1 - slowest, 0x3F - fastest. + // Slide the already playing note pitch towards another one by xx Hz every tick. + // The note value is saved until the note is playing, so you don't have to repeat the note value to continue sliding. + EffectSlideToNote = 0x03, + + // 4xy, x - vibrato speed (0..7), y - vibrato depth (0..7). + // Vibrato effect. The pitch of the note increases by x Hz each tick to a positive vibrato depth, then decreases to a negative depth. + // Value 1 of depth means 1/7 of a semitone (about 14.28 ct), so value 7 means full semitone. + // Note will play without vibrato on the first tick at the beginning of the effect. + // Vibrato speed and depth are saved until the note is playing, and will be updated only if they are not zero, so you doesn't have to repeat them every tick. + EffectVibrato = 0x04, + + // Effect05 = 0x05, + // Effect06 = 0x06, + // Effect07 = 0x07, + // Effect08 = 0x08, + // Effect09 = 0x09, + // Effect0A = 0x0A, + + // Bxx, xx - pattern number + // Jump to the order xx in the pattern order table at first tick of current row. + // So if you want to jump to the pattern after note 4, you should put this effect on the 5th note. + EffectJumpToOrder = 0x0B, + + // Cxx, xx - pwm value + // Set the PWM value to xx for current row. + EffectPWM = 0x0C, + + // Bxx, xx - row number + // Jump to the row xx in next pattern at first tick of current row. + // So if you want to jump to the pattern after note 4, you should put this effect on the 5th note. + EffectBreakPattern = 0x0D, + + // Effect0E = 0x0E, + + // Fxx, xx - song speed, 0 - 1 tick per note, 1 - 2 ticks per note, 0x3F - 64 ticks per note. + // Set the speed of the song in terms of ticks per note. + // Will be applied at the first tick of current row. + EffectSetSpeed = 0x0F, +} Effect; + +#define EFFECT_DATA_2(x, y) ((x) | ((y) << 3)) +#define EFFECT_DATA_GET_X(data) ((data)&0x07) +#define EFFECT_DATA_GET_Y(data) (((data) >> 3) & 0x07) +#define EFFECT_DATA_NONE 0 +#define EFFECT_DATA_1_MAX 0x3F +#define EFFECT_DATA_2_MAX 0x07 + +#define FREQUENCY_UNSET -1.0f + +#define PWM_MIN 0.01f +#define PWM_MAX 0.5f +#define PWM_DEFAULT PWM_MAX + +#define PATTERN_SIZE 64 + +#define ROW_MAKE(note, effect, data) \ + ((Row)(((note)&0x3F) | (((effect)&0xF) << 6) | (((data)&0x3F) << 10))) + +typedef struct { + Row rows[PATTERN_SIZE]; +} Channel; + +typedef struct { + Channel* channels; +} Pattern; + +typedef struct { + uint8_t channels_count; + uint8_t patterns_count; + Pattern* patterns; + uint8_t order_list_size; + uint8_t* order_list; + uint16_t ticks_per_second; +} Song; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/view/tracker_view.c b/Applications/Official/DEV_FW/source/xMasterX/musictracker/view/tracker_view.c new file mode 100644 index 000000000..87e6b0fcf --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/musictracker/view/tracker_view.c @@ -0,0 +1,182 @@ +#include "tracker_view.h" +#include +#include + +typedef struct { + const Song* song; + uint8_t order_list_index; + uint8_t row; +} TrackerViewModel; + +struct TrackerView { + View* view; + void* back_context; + TrackerViewCallback back_callback; +}; + +static Channel* get_current_channel(TrackerViewModel* model) { + uint8_t channel_id = 0; + uint8_t pattern_id = model->song->order_list[model->order_list_index]; + Pattern* pattern = &model->song->patterns[pattern_id]; + return &pattern->channels[channel_id]; +} + +static const char* get_note_from_id(uint8_t note) { +#define NOTE_COUNT 12 + const char* notes[NOTE_COUNT] = { + "C ", + "C#", + "D ", + "D#", + "E ", + "F ", + "F#", + "G ", + "G#", + "A ", + "A#", + "B ", + }; + return notes[(note) % NOTE_COUNT]; +#undef NOTE_COUNT +} + +static uint8_t get_octave_from_id(uint8_t note) { + return ((note) / 12) + 2; +} + +static uint8_t get_first_row_id(uint8_t row) { + return (row / 10) * 10; +} + +static void + draw_row(Canvas* canvas, uint8_t i, Channel* channel, uint8_t row, FuriString* buffer) { + uint8_t x = 12 * (i + 1); + uint8_t first_row_id = get_first_row_id(row); + uint8_t current_row_id = first_row_id + i; + + if((current_row_id) >= 64) { + return; + } + + Row current_row = channel->rows[current_row_id]; + uint8_t note = current_row & ROW_NOTE_MASK; + uint8_t effect = (current_row >> 6) & ROW_EFFECT_MASK; + uint8_t data = (current_row >> 10) & ROW_EFFECT_DATA_MASK; + + if(current_row_id == row) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_line(canvas, x - 9, 1, x - 9, 62); + canvas_draw_box(canvas, x - 8, 0, 9, 64); + canvas_draw_line(canvas, x + 1, 1, x + 1, 62); + canvas_set_color(canvas, ColorWhite); + } + + furi_string_printf(buffer, "%02X", current_row_id); + canvas_draw_str(canvas, x, 61, furi_string_get_cstr(buffer)); + + if(note > 0 && note < NOTE_OFF) { + furi_string_printf( + buffer, "%s%d", get_note_from_id(note - 1), get_octave_from_id(note - 1)); + canvas_draw_str(canvas, x, 44, furi_string_get_cstr(buffer)); + } else if(note == NOTE_OFF) { + canvas_draw_str(canvas, x, 44, "OFF"); + } else { + canvas_draw_str(canvas, x, 44, "---"); + } + + if(effect == 0 && data == 0) { + canvas_draw_str(canvas, x, 21, "-"); + canvas_draw_str(canvas, x, 12, "--"); + } else { + furi_string_printf(buffer, "%X", effect); + canvas_draw_str(canvas, x, 21, furi_string_get_cstr(buffer)); + + if(effect == EffectArpeggio || effect == EffectVibrato) { + uint8_t data_x = EFFECT_DATA_GET_X(data); + uint8_t data_y = EFFECT_DATA_GET_Y(data); + furi_string_printf(buffer, "%d%d", data_x, data_y); + canvas_draw_str(canvas, x, 12, furi_string_get_cstr(buffer)); + } else { + furi_string_printf(buffer, "%02X", data); + canvas_draw_str(canvas, x, 12, furi_string_get_cstr(buffer)); + } + } + + if(current_row_id == row) { + canvas_set_color(canvas, ColorBlack); + } +} + +static void tracker_view_draw_callback(Canvas* canvas, void* _model) { + TrackerViewModel* model = _model; + if(model->song == NULL) { + return; + } + + canvas_set_font_direction(canvas, CanvasDirectionBottomToTop); + canvas_set_font(canvas, FontKeyboard); + + Channel* channel = get_current_channel(model); + FuriString* buffer = furi_string_alloc(); + + for(uint8_t i = 0; i < 10; i++) { + draw_row(canvas, i, channel, model->row, buffer); + } + furi_string_free(buffer); +} + +static bool tracker_view_input_callback(InputEvent* event, void* context) { + TrackerView* tracker_view = context; + + if(tracker_view->back_callback) { + if(event->type == InputTypeShort && event->key == InputKeyBack) { + tracker_view->back_callback(tracker_view->back_context); + return true; + } + } + return false; +} + +TrackerView* tracker_view_alloc() { + TrackerView* tracker_view = malloc(sizeof(TrackerView)); + tracker_view->view = view_alloc(); + view_allocate_model(tracker_view->view, ViewModelTypeLocking, sizeof(TrackerViewModel)); + view_set_context(tracker_view->view, tracker_view); + view_set_draw_callback(tracker_view->view, (ViewDrawCallback)tracker_view_draw_callback); + view_set_input_callback(tracker_view->view, (ViewInputCallback)tracker_view_input_callback); + return tracker_view; +} + +void tracker_view_free(TrackerView* tracker_view) { + view_free(tracker_view->view); + free(tracker_view); +} + +View* tracker_view_get_view(TrackerView* tracker_view) { + return tracker_view->view; +} + +void tracker_view_set_back_callback( + TrackerView* tracker_view, + TrackerViewCallback callback, + void* context) { + tracker_view->back_callback = callback; + tracker_view->back_context = context; +} + +void tracker_view_set_song(TrackerView* tracker_view, const Song* song) { + with_view_model( + tracker_view->view, TrackerViewModel * model, { model->song = song; }, true); +} + +void tracker_view_set_position(TrackerView* tracker_view, uint8_t order_list_index, uint8_t row) { + with_view_model( + tracker_view->view, + TrackerViewModel * model, + { + model->order_list_index = order_list_index; + model->row = row; + }, + true); +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/view/tracker_view.h b/Applications/Official/DEV_FW/source/xMasterX/musictracker/view/tracker_view.h new file mode 100644 index 000000000..6c2e69ba4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/musictracker/view/tracker_view.h @@ -0,0 +1,29 @@ +#include +#include "../tracker_engine/tracker.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct TrackerView TrackerView; + +TrackerView* tracker_view_alloc(); + +void tracker_view_free(TrackerView* tracker_view); + +View* tracker_view_get_view(TrackerView* tracker_view); + +typedef void (*TrackerViewCallback)(void* context); + +void tracker_view_set_back_callback( + TrackerView* tracker_view, + TrackerViewCallback callback, + void* context); + +void tracker_view_set_song(TrackerView* tracker_view, const Song* song); + +void tracker_view_set_position(TrackerView* tracker_view, uint8_t order_list_index, uint8_t row); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/zero_tracker.c b/Applications/Official/DEV_FW/source/xMasterX/musictracker/zero_tracker.c new file mode 100644 index 000000000..f4c10d9ef --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/musictracker/zero_tracker.c @@ -0,0 +1,536 @@ +#include +#include +#include +#include +#include "zero_tracker.h" +#include "tracker_engine/tracker.h" +#include "view/tracker_view.h" + +// Channel p_0_channels[] = { +// { +// .rows = +// { +// // 1/4 +// ROW_MAKE(NOTE_C3, EffectArpeggio, EFFECT_DATA_2(4, 7)), +// ROW_MAKE(0, EffectArpeggio, EFFECT_DATA_2(4, 7)), +// ROW_MAKE(NOTE_C4, EffectSlideToNote, 0x20), +// ROW_MAKE(0, EffectSlideToNote, 0x20), +// // +// ROW_MAKE(0, EffectSlideToNote, 0x20), +// ROW_MAKE(0, EffectSlideToNote, 0x20), +// ROW_MAKE(0, EffectSlideToNote, 0x20), +// ROW_MAKE(0, EffectSlideToNote, 0x20), +// // +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), +// // +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), +// // 2/4 +// ROW_MAKE(NOTE_C3, EffectSlideDown, 0x20), +// ROW_MAKE(0, EffectSlideDown, 0x20), +// ROW_MAKE(NOTE_C4, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// // +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// // +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// // +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(3, 3)), +// ROW_MAKE(NOTE_OFF, EffectVibrato, EFFECT_DATA_2(3, 3)), +// // 3/4 +// ROW_MAKE(NOTE_C3, EffectArpeggio, EFFECT_DATA_2(4, 7)), +// ROW_MAKE(0, EffectArpeggio, EFFECT_DATA_2(4, 7)), +// ROW_MAKE(NOTE_OFF, 0, 0), +// ROW_MAKE(0, 0, 0), +// // +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// // +// ROW_MAKE(NOTE_C2, EffectPWM, 60), +// ROW_MAKE(0, EffectPWM, 32), +// ROW_MAKE(0, EffectPWM, 12), +// ROW_MAKE(NOTE_OFF, 0, 0), +// // +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// // 4/4 +// ROW_MAKE(NOTE_C3, EffectSlideDown, 0x20), +// ROW_MAKE(0, EffectSlideDown, 0x20), +// ROW_MAKE(0, EffectSlideDown, 0x20), +// ROW_MAKE(NOTE_OFF, 0, 0), +// // +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// // +// ROW_MAKE(NOTE_C2, EffectPWM, 60), +// ROW_MAKE(0, EffectPWM, 32), +// ROW_MAKE(0, EffectPWM, 12), +// ROW_MAKE(NOTE_OFF, 0, 0), +// // +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// ROW_MAKE(0, 0, 0), +// }, +// }, +// }; + +Channel p_0_channels[] = { + { + .rows = + { + // + ROW_MAKE(NOTE_A4, EffectArpeggio, EFFECT_DATA_2(4, 7)), + ROW_MAKE(NOTE_C3, 0, 0), + ROW_MAKE(NOTE_F2, 0, 0), + ROW_MAKE(NOTE_C3, 0, 0), + // + ROW_MAKE(NOTE_E4, 0, 0), + ROW_MAKE(NOTE_C3, 0, 0), + ROW_MAKE(NOTE_E4, EffectPWM, 50), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_E5, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_C3, EffectSlideDown, 0x30), + ROW_MAKE(NOTE_F2, 0, 0), + ROW_MAKE(NOTE_C3, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_C3, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + // + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_B4, EffectArpeggio, EFFECT_DATA_2(4, 7)), + ROW_MAKE(NOTE_D3, 0, 0), + ROW_MAKE(NOTE_G2, 0, 0), + ROW_MAKE(NOTE_D3, 0, 0), + // + ROW_MAKE(NOTE_E4, 0, 0), + ROW_MAKE(NOTE_D3, 0, 0), + ROW_MAKE(NOTE_E4, EffectPWM, 50), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_E5, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_D3, EffectSlideDown, 0x3F), + ROW_MAKE(NOTE_G2, 0, 0), + ROW_MAKE(NOTE_D3, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_D3, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + // + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(NOTE_OFF, 0, 0), + }, + }, +}; + +Channel p_1_channels[] = { + { + .rows = + { + // + ROW_MAKE(NOTE_C5, EffectArpeggio, EFFECT_DATA_2(4, 7)), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 50), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_G4, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C6, 0, 0), + ROW_MAKE(NOTE_E3, EffectSlideDown, 0x30), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 50), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_G4, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + // + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C5, EffectArpeggio, EFFECT_DATA_2(4, 7)), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 50), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_G4, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(0, EffectPWM, 55), + ROW_MAKE(0, EffectPWM, 45), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C6, 0, 0), + ROW_MAKE(NOTE_E3, EffectSlideDown, 0x30), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 50), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_G4, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, 0, 0), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + // + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(1, 1)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(0, EffectVibrato, EFFECT_DATA_2(2, 2)), + ROW_MAKE(NOTE_OFF, 0, 0), + }, + }, +}; + +Channel p_2_channels[] = { + { + .rows = + { + // + ROW_MAKE(NOTE_C5, EffectArpeggio, EFFECT_DATA_2(4, 7)), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, 0, 0), + // + ROW_MAKE(NOTE_C5, EffectPWM, 55), + ROW_MAKE(NOTE_A4, EffectPWM, 45), + ROW_MAKE(NOTE_C5, EffectPWM, 35), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(NOTE_C5, EffectPWM, 55), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_E3, EffectSlideDown, 0x30), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_OFF, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 55), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_D5, EffectPWM, 55), + ROW_MAKE(NOTE_B4, EffectPWM, 55), + // + ROW_MAKE(NOTE_D5, EffectPWM, 45), + ROW_MAKE(NOTE_B4, EffectPWM, 45), + ROW_MAKE(NOTE_D5, EffectPWM, 35), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, EffectArpeggio, EFFECT_DATA_2(4, 7)), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_E5, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_E5, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + // + ROW_MAKE(NOTE_E5, EffectPWM, 55), + ROW_MAKE(NOTE_C5, EffectPWM, 45), + ROW_MAKE(NOTE_E5, EffectPWM, 35), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_E5, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_E5, EffectPWM, 55), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_E3, EffectSlideDown, 0x30), + ROW_MAKE(NOTE_A2, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + // + ROW_MAKE(NOTE_OFF, 0, 0), + ROW_MAKE(NOTE_E3, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 55), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_D5, EffectPWM, 55), + ROW_MAKE(NOTE_B4, EffectPWM, 55), + // + ROW_MAKE(NOTE_D5, EffectPWM, 45), + ROW_MAKE(NOTE_B4, EffectPWM, 45), + ROW_MAKE(NOTE_D5, EffectPWM, 35), + ROW_MAKE(NOTE_OFF, 0, 0), + }, + }, +}; + +Channel p_3_channels[] = { + { + .rows = + { + // + ROW_MAKE(NOTE_Ds5, EffectArpeggio, EFFECT_DATA_2(4, 6)), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_Ds5, 0, 0), + ROW_MAKE(NOTE_C5, EffectPWM, 55), + // + ROW_MAKE(NOTE_Ds5, EffectPWM, 45), + ROW_MAKE(NOTE_C5, EffectPWM, 35), + ROW_MAKE(NOTE_Ds5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 55), + // + ROW_MAKE(NOTE_D5, EffectPWM, 45), + ROW_MAKE(NOTE_B4, EffectPWM, 35), + ROW_MAKE(NOTE_D5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_Cs5, EffectArpeggio, EFFECT_DATA_2(4, 6)), + ROW_MAKE(NOTE_As4, 0, 0), + ROW_MAKE(NOTE_Cs5, 0, 0), + ROW_MAKE(NOTE_As4, EffectPWM, 55), + // + ROW_MAKE(NOTE_Cs5, EffectPWM, 45), + ROW_MAKE(NOTE_As4, EffectPWM, 35), + ROW_MAKE(NOTE_Cs5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, EffectPWM, 55), + // + ROW_MAKE(NOTE_C5, EffectPWM, 45), + ROW_MAKE(NOTE_A4, EffectPWM, 35), + ROW_MAKE(NOTE_C5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_B4, EffectArpeggio, EFFECT_DATA_2(4, 6)), + ROW_MAKE(NOTE_Gs4, 0, 0), + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_Gs4, EffectPWM, 55), + // + ROW_MAKE(NOTE_B4, EffectPWM, 45), + ROW_MAKE(NOTE_Gs4, EffectPWM, 35), + ROW_MAKE(NOTE_B4, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, 0, 0), + ROW_MAKE(NOTE_C5, 0, 0), + ROW_MAKE(NOTE_A4, EffectPWM, 55), + // + ROW_MAKE(NOTE_C5, EffectPWM, 45), + ROW_MAKE(NOTE_A4, EffectPWM, 35), + ROW_MAKE(NOTE_C5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_Cs5, EffectArpeggio, EFFECT_DATA_2(4, 6)), + ROW_MAKE(NOTE_As4, 0, 0), + ROW_MAKE(NOTE_Cs5, 0, 0), + ROW_MAKE(NOTE_As4, EffectPWM, 55), + // + ROW_MAKE(NOTE_Cs5, EffectPWM, 45), + ROW_MAKE(NOTE_As4, EffectPWM, 35), + ROW_MAKE(NOTE_Cs5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + // + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_B4, 0, 0), + ROW_MAKE(NOTE_D5, 0, 0), + ROW_MAKE(NOTE_B4, EffectPWM, 55), + // + ROW_MAKE(NOTE_D5, EffectPWM, 45), + ROW_MAKE(NOTE_B4, EffectPWM, 35), + ROW_MAKE(NOTE_D5, EffectPWM, 30), + ROW_MAKE(NOTE_OFF, 0, 0), + }, + }, +}; +Pattern patterns[] = { + {.channels = p_0_channels}, + {.channels = p_1_channels}, + {.channels = p_2_channels}, + {.channels = p_3_channels}, +}; + +uint8_t order_list[] = { + 0, + 1, + 0, + 2, + 0, + 1, + 0, + 3, +}; + +Song song = { + .channels_count = 1, + .patterns_count = sizeof(patterns) / sizeof(patterns[0]), + .patterns = patterns, + + .order_list_size = sizeof(order_list) / sizeof(order_list[0]), + .order_list = order_list, + + .ticks_per_second = 60, +}; + +void tracker_message(TrackerMessage message, void* context) { + FuriMessageQueue* queue = context; + furi_assert(queue); + furi_message_queue_put(queue, &message, 0); +} + +int32_t zero_tracker_app(void* p) { + UNUSED(p); + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_display_backlight_enforce_on); + + Gui* gui = furi_record_open(RECORD_GUI); + ViewDispatcher* view_dispatcher = view_dispatcher_alloc(); + TrackerView* tracker_view = tracker_view_alloc(); + tracker_view_set_song(tracker_view, &song); + view_dispatcher_add_view(view_dispatcher, 0, tracker_view_get_view(tracker_view)); + view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); + view_dispatcher_switch_to_view(view_dispatcher, 0); + + FuriMessageQueue* queue = furi_message_queue_alloc(8, sizeof(TrackerMessage)); + Tracker* tracker = tracker_alloc(); + tracker_set_message_callback(tracker, tracker_message, queue); + tracker_set_song(tracker, &song); + tracker_start(tracker); + + while(1) { + TrackerMessage message; + FuriStatus status = furi_message_queue_get(queue, &message, portMAX_DELAY); + if(status == FuriStatusOk) { + if(message.type == TrackerPositionChanged) { + uint8_t order_list_index = message.data.position.order_list_index; + uint8_t row = message.data.position.row; + uint8_t pattern = song.order_list[order_list_index]; + tracker_view_set_position(tracker_view, order_list_index, row); + FURI_LOG_I("Tracker", "O:%d P:%d R:%d", order_list_index, pattern, row); + } else if(message.type == TrackerEndOfSong) { + FURI_LOG_I("Tracker", "End of song"); + break; + } + } + } + + tracker_stop(tracker); + tracker_free(tracker); + furi_message_queue_free(queue); + + furi_delay_ms(500); + + view_dispatcher_remove_view(view_dispatcher, 0); + tracker_view_free(tracker_view); + view_dispatcher_free(view_dispatcher); + + notification_message(notification, &sequence_display_backlight_enforce_auto); + + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/zero_tracker.h b/Applications/Official/DEV_FW/source/xMasterX/musictracker/zero_tracker.h new file mode 100644 index 000000000..e69de29bb diff --git a/Applications/Official/DEV_FW/source/xMasterX/musictracker/zero_tracker.png b/Applications/Official/DEV_FW/source/xMasterX/musictracker/zero_tracker.png new file mode 100644 index 000000000..61488d153 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/musictracker/zero_tracker.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Distr/Nrf24_Scanner.fap b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Distr/Nrf24_Scanner.fap new file mode 100644 index 000000000..716e41397 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Distr/Nrf24_Scanner.fap differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Distr/nrf24scan/addr-WCO1.txt b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Distr/nrf24scan/addr-WCO1.txt new file mode 100644 index 000000000..658340d03 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Distr/nrf24scan/addr-WCO1.txt @@ -0,0 +1,7 @@ +Rate: 1 +Ch: 120 +ESB: 1 +DPL: 1 +CRC: 2 +Payload: 4 +P0: C8C8E5 diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Distr/nrf24scan/addresses.txt b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Distr/nrf24scan/addresses.txt new file mode 100644 index 000000000..e5bd3ed32 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Distr/nrf24scan/addresses.txt @@ -0,0 +1,11 @@ +Rate: 1 +Ch: 2 +ESB: 1 +DPL: 0 +CRC: 2 +Payload: 4 +P0: C8C8C0 +P1: C8C8C1 +P2: C2 +P3: C3 +P4: E5 diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Distr/nrf24scan/sniff.txt b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Distr/nrf24scan/sniff.txt new file mode 100644 index 000000000..fa8c5ba9f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Distr/nrf24scan/sniff.txt @@ -0,0 +1,5 @@ +SNIFF +ESB: 1 +CRC: 2 +P0: 00AA +P1: 0055 diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/README.md b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/README.md new file mode 100644 index 000000000..a93ec913e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/README.md @@ -0,0 +1,110 @@ +# NRF24 scanner with logging and resend ability for Flipper Zero + +An [NRF24](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf) driver for the [Flipper Zero](https://flipperzero.one/) device. The NRF24 is a popular line of 2.4GHz radio transceivers from Nordic Semiconductors.
+NRF24L01+ Enhanced ShockBurst packet decoder example using Python: [nrf24_packet_decoder.py](https://raw.githubusercontent.com/vad7/nrf24scan/master/nrf24_packet_decoder.py)
+
+Flipper Zero FAP file: [Nrf24_Scanner.fap](https://raw.githubusercontent.com/vad7/nrf24scan/master/Nrf24_Scanner.fap) +

+___________________________________________________________________________ +
+Приложение для Flipper Zero, читающее эфир на радиомодулях nRF24L01.
+Выбор пукта меню - стрелки вверх/вниз, стрелки влево/вправо либо изменют настройки либо управляют видом списка, кнопка ОК либо выбирает режим (короткое нажатие), либо выполняет дополнительное действие (длительное нажатие).

+
+По умолчанию при запуске включается режим поиска (sniff) - ищутся все валидные пакеты с корректным заголовком и CRC.
+Размер CRC и тип пакета (Enhanced ShockBurst или нет) задается. CRC может быть или 1 или 2 байта.
+Так как пакеты читаются в RAW формате, то длина полезной нагрузки не может быть больше 23 байт, пакеты с большей длинной не будут пойманы.
+В настройках задается минимальный размер нагрузки (payload)
+После принятия, пакет сдвигается побитно и валидируется. Побитный сдвиг сильно увеличивает вероятность отлова пакета, но так же увеличивается количество мусорных пакетов.
+Количество уникальных адресов запоминается (просмотр - стрелка вниз в режиме просмотра адресов)
+После поиска можно переключиться в режим сканирования по найденным адресам или сканировать адрес конкретного пакета или группы адресов с различным LSB в адресе
+
+ +
+Адреса, которые попались дважды и более раз отображаются списком:
+ +

+Изменение режима sniff/scan - стрелками на пункте Scan.

+Режим сканирования (scan) - просто чтение пакетов по заданным в настройках мак адресам и виду пакета - ESB/DPL.
+На начальном экране в режиме чтения можно загрузить файл настроек (по умолчанию загружается settings.txt из папки nrf24_scanner на SD карте).
+В файле настройке задаются адреса (максимум 6) в шестнадцатеричном виде (старший байт - первый), длина адреса вычисляется по P0.
+Остальные настройки можно поменять интерактивно
+Настройки сохраняются длительным нажатием на ОК.

+Описание настроек:
+Ch - номер канала.
+Rate - скорость передачи данных
+Next Ch time - через сколько секунд будет увеличен номер канала
+Log - выбор режима авто сохраннения в файлы log-xx.txt. Yes - сохранять в новый файл при заполнении буфера в 99 записей, Append - добавлять в последний файл, Clear - только очистка буфера

+В пунктах Ch, Rate, Next при нажатии OK меняются параметры связи:
+ESB - Enhanced ShockBurst (включена автоотправка подтверждения получения пакета, работающий приемник тоже попытается это сделать, возможны коллизии)
+DPL - Динамический пакет
+CRC1/2 - Размер CRC в байтах
+Payload - размер пакета в байтах
+
+Просмотр принятых пакетов

+ +
+В пункте "Start scan/sniff" можно выбрать стрелками сканировать и смотреть или просто смотреть (view).
+Если в файле настройки было несколько адресов, то первая цифра - номер канала (pipe) от 0 до 5.
+Стрелки - перемещение по списку и горизонтальное скролирование
+Долгий OK - отправка пакета
+OK - вход в режим просмотра адресов и включения декодирования заголовка ESB пакета и CRC.
+При декодировании заголовка (PCF) - первые 2 цифры - длина пакета в hex или 33, если длина пакета фиксирована
+3-я цифра - PID (2bit) << 1 + флаг NO_ACK
+Если включен режим декодирования CRC, то по всему пакету ищется подходящая CRC и подчеркивается в списке, а так же вместо ":" выводится "=" после номера записи в буфере
+
+ +
+ +
+ +
+
+_________________________________________________________________________________ +
+
+Settings file (default addr.txt) format:
+ +Rate: 0/1/2 - rate in Mbps (=0.25/1/2)
+Ch: 0..125 - default channel
+ESB: 0/1 (1 - Enhanced ShockBurst)
+DPL: 0/1 (1 - Dynamic Payload Length)
+CRC: 0/1/2 (CRC length)
+Payload: 1..32 (bytes)
+P0: address pipe #0 in hex (max 5 bytes, LSB last)
+P1: address pipe #1 in hex (max 5 bytes, LSB last)
+P2: address pipe #2, LSB in hex (1 byte)
+P3: address pipe #3, LSB in hex (1 byte)
+P4: address pipe #4, LSB in hex (1 byte)
+P5: address pipe #5, LSB in hex (1 byte)
+captured data in raw format, first byte = address # 0..5, Payload len if DPL
+... up to MAX_LOG_RECORDS-1
+
+In the list of the received:
+Press OK - send the packet,
+Long press OK - view addresses.
+
+Decode the Packet Control Field and check CRC (long press OK in the list and then press '<' / '>').
+ESB (Enhanced Shockburst) option must be turned off. +Press '>' to decode CRC.
+1 - pipe #
+2 - Payload length (for valide packet must be 1..20 or 33 hex)
+3 - PID (2 bit) + NO_ACK (1 bit)
+
+
+
+## PinOut from from NoComp/Frog + + +# NRF24 pinout by UberGuidoZ +2/A7 on FZ goes to MOSI/6 on nrf24l01
+3/A6 on FZ goes to MISO/7 on nrf24l01
+4/A4 on FZ goes to CSN/4 on nrf24l01
+5/B3 on FZ goes to SCK/5 on nrf24l01
+6/B2 on FZ goes to CE/3 on nrf24l01
+8/GND on FZ goes to GND/1 on nrf24l01
+9/3V3 on FZ goes to VCC/2 on nrf24l01
+IRQ/8 is left disconnected on nrf24l01 +![NRF_Pins](https://user-images.githubusercontent.com/57457139/178093717-39effd5c-ebe2-4253-b13c-70517d7902f9.png) +If the nRF module is acting a bit flakey, try adding a capacitor to the vcc/gnd lines! I've not tried the Plus model so it may have a bigger need for a cap. Otherwise, I haven't had any major issues. Anything from a 3.3 uF to 10 uF should do. (Watch your positive/negative placement! Negative to ground.) I learned if you wanna get fancy, include a 0.1 uF cap in parallel. The 3.3 uF to 10 uF will respond to slow freq changes while the 0.1 uF will respond to the high freq switching spikes that the larger one cannot. That said, a single 10 uF will likely suffice for the Mousejack attack. ¯\\\_(ツ)_/¯ +![NRF_Capacitor](https://user-images.githubusercontent.com/57457139/178169959-d030f9a6-d2ac-46af-af8b-470ff092c8a7.jpg) + diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-1.png b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-1.png new file mode 100644 index 000000000..61ca892d8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-2.png b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-2.png new file mode 100644 index 000000000..0e3ebf5b2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-3.png b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-3.png new file mode 100644 index 000000000..651052350 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-4.png b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-4.png new file mode 100644 index 000000000..6351cdd3f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-5.png b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-5.png new file mode 100644 index 000000000..d9e3173a2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-5.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-6.png b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-6.png new file mode 100644 index 000000000..7c9ecfc34 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-6.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-7.png b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-7.png new file mode 100644 index 000000000..579a88e06 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/Screenshot-7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/application.fam b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/application.fam new file mode 100644 index 000000000..09f9fcab8 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/application.fam @@ -0,0 +1,20 @@ +App( + appid="Nrf24_Scanner", + name="[NRF24] Scanner", + apptype=FlipperAppType.EXTERNAL, + entry_point="nrf24scan_app", + cdefines=["APP_NRF24SCAN"], + requires=["gui"], + stack_size=2 * 1024, + order=60, + fap_icon="nrf24scan_10px.png", + fap_category="GPIO_Extra", + fap_private_libs=[ + Lib( + name="nrf24", + sources=[ + "nrf24.c", + ], + ), + ], +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/lib/nrf24/nrf24.c b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/lib/nrf24/nrf24.c new file mode 100644 index 000000000..83f0613a1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/lib/nrf24/nrf24.c @@ -0,0 +1,533 @@ +// Modified by vad7, 25.11.2022 +// +#include "nrf24.h" +#include +#include +#include +#include +#include + +void nrf24_init() { + furi_hal_spi_bus_handle_init(nrf24_HANDLE); + furi_hal_spi_acquire(nrf24_HANDLE); + furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_gpio_write(nrf24_CE_PIN, false); +} + +void nrf24_deinit() { + furi_hal_spi_release(nrf24_HANDLE); + furi_hal_spi_bus_handle_deinit(nrf24_HANDLE); + furi_hal_gpio_write(nrf24_CE_PIN, false); + furi_hal_gpio_init(nrf24_CE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +void nrf24_spi_trx( + FuriHalSpiBusHandle* handle, + uint8_t* tx, + uint8_t* rx, + uint8_t size, + uint32_t timeout) { + UNUSED(timeout); + furi_hal_gpio_write(handle->cs, false); + furi_hal_spi_bus_trx(handle, tx, rx, size, nrf24_TIMEOUT); + furi_hal_gpio_write(handle->cs, true); +} + +uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) { + uint8_t tx[2] = {W_REGISTER | (REGISTER_MASK & reg), data}; + uint8_t rx[2] = {0}; + nrf24_spi_trx(handle, tx, rx, 2, nrf24_TIMEOUT); + //FURI_LOG_D("NRF_WR", " #%02X=%02X", reg, data); + return rx[0]; +} + +uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(rx, 0, size + 1); + tx[0] = W_REGISTER | (REGISTER_MASK & reg); + memcpy(&tx[1], data, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + //FURI_LOG_D("NRF_WR", " #%02X(%02X)=0x%02X%02X%02X%02X%02X", reg, size, data[0], data[1], data[2], data[3], data[4] ); + return rx[0]; +} + +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(rx, 0, size + 1); + tx[0] = R_REGISTER | (REGISTER_MASK & reg); + memset(&tx[1], 0, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + memcpy(data, &rx[1], size); + return rx[0]; +} + +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_RX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_TX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle) { + uint8_t maclen; + nrf24_read_reg(handle, REG_SETUP_AW, &maclen, 1); + maclen &= 3; + return maclen + 2; +} + +uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen) { + assert(maclen > 1 && maclen < 6); + uint8_t status = 0; + status = nrf24_write_reg(handle, REG_SETUP_AW, maclen - 2); + return status; +} + +uint8_t nrf24_status(FuriHalSpiBusHandle* handle) { + uint8_t status; + uint8_t tx[] = {R_REGISTER | (REGISTER_MASK & REG_STATUS)}; + nrf24_spi_trx(handle, tx, &status, 1, nrf24_TIMEOUT); + return status; +} + +uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle) { + uint8_t setup = 0; + uint32_t rate = 0; + nrf24_read_reg(handle, REG_RF_SETUP, &setup, 1); + setup &= 0x28; + if(setup == 0x20) + rate = 250000; // 250kbps + else if(setup == 0x08) + rate = 2000000; // 2Mbps + else if(setup == 0x00) + rate = 1000000; // 1Mbps + + return rate; +} + +uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate) { + uint8_t r6 = 0; + uint8_t status = 0; + if(!rate) rate = 2000000; + + nrf24_read_reg(handle, REG_RF_SETUP, &r6, 1); // RF_SETUP register + r6 = r6 & (~0x28); // Clear rate fields. + if(rate == 2000000) + r6 = r6 | 0x08; + else if(rate == 1000000) + r6 = r6; + else if(rate == 250000) + r6 = r6 | 0x20; + + status = nrf24_write_reg(handle, REG_RF_SETUP, r6); // Write new rate. + return status; +} + +uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle) { + uint8_t channel = 0; + nrf24_read_reg(handle, REG_RF_CH, &channel, 1); + return channel; +} + +uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan) { + uint8_t status; + status = nrf24_write_reg(handle, REG_RF_CH, chan); + return status; +} + +uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { + uint8_t size = 0; + uint8_t status = 0; + size = nrf24_get_maclen(handle); + status = nrf24_read_reg(handle, REG_RX_ADDR_P0, mac, size); + return status; +} + +uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { + uint8_t status = 0; + uint8_t clearmac[] = {0, 0, 0, 0, 0}; + nrf24_set_maclen(handle, size); + nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, clearmac, 5); + status = nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, mac, size); + return status; +} + +uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { + uint8_t size = 0; + uint8_t status = 0; + size = nrf24_get_maclen(handle); + status = nrf24_read_reg(handle, REG_TX_ADDR, mac, size); + return status; +} + +uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { + uint8_t status = 0; + uint8_t clearmac[] = {0, 0, 0, 0, 0}; + nrf24_set_maclen(handle, size); + nrf24_write_buf_reg(handle, REG_TX_ADDR, clearmac, 5); + status = nrf24_write_buf_reg(handle, REG_TX_ADDR, mac, size); + return status; +} + +uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle, uint8_t pipe) { + uint8_t len = 0; + if(pipe > 5) pipe = 0; + nrf24_read_reg(handle, RX_PW_P0 + pipe, &len, 1); + return len; +} + +uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len) { + uint8_t status = 0; + status = nrf24_write_reg(handle, RX_PW_P0, len); + return status; +} + +uint8_t nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* ret_packetsize, uint8_t packet_size) { + uint8_t status = 0; + uint8_t tx_cmd[33] = {0}; // 32 max payload size + 1 for command + uint8_t tmp_packet[33] = {0}; + + status = nrf24_status(handle); + if(!(status & RX_DR)) { + tx_cmd[0] = R_REGISTER | (REGISTER_MASK & REG_FIFO_STATUS); + nrf24_spi_trx(handle, tx_cmd, tmp_packet, 2, nrf24_TIMEOUT); + if((tmp_packet[1] & 1) == 0) status |= RX_DR; // packet in FIFO buffer + } + if(status & RX_DR) { + if(packet_size == 1) + packet_size = nrf24_get_packetlen(handle, (status >> 1) & 7); + else if(packet_size == 0){ + tx_cmd[0] = R_RX_PL_WID; tx_cmd[1] = 0; + nrf24_spi_trx(handle, tx_cmd, tmp_packet, 2, nrf24_TIMEOUT); + packet_size = tmp_packet[1]; + } + if(packet_size > 32 || packet_size == 0) packet_size = 32; + tx_cmd[0] = R_RX_PAYLOAD; tx_cmd[1] = 0; + nrf24_spi_trx(handle, tx_cmd, tmp_packet, packet_size + 1, nrf24_TIMEOUT); + memcpy(packet, &tmp_packet[1], packet_size); + nrf24_write_reg(handle, REG_STATUS, RX_DR); // clear RX_DR + } else if(status & (TX_DS | MAX_RT)) { // MAX_RT, TX_DS + nrf24_write_reg(handle, REG_STATUS, (TX_DS | MAX_RT)); // clear RX_DR, MAX_RT. + } + + *ret_packetsize = packet_size; + return status; +} + +// Return 0 when error +uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack) { + uint8_t status = 0; + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(tx, 0, size + 1); + memset(rx, 0, size + 1); + + if(!ack) + tx[0] = W_TX_PAYLOAD_NOACK; + else + tx[0] = W_TX_PAYLOAD; + + memcpy(&tx[1], payload, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + nrf24_set_tx_mode(handle); + + uint32_t start_time = furi_get_tick(); + while(!(status & (TX_DS | MAX_RT)) && furi_get_tick() - start_time < 2000UL) status = nrf24_status(handle); + + if(status & MAX_RT) nrf24_flush_tx(handle); + + nrf24_set_idle(handle); + nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT); + return status & TX_DS; +} + +uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg = cfg | 2; + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_delay_ms(1000); + return status; +} + +uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg &= 0xfc; // clear bottom two bits to power down the radio + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + //nr204_write_reg(handle, REG_EN_RXADDR, 0x0); + furi_hal_gpio_write(nrf24_CE_PIN, false); + return status; +} + +uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + //status = nrf24_write_reg(handle, REG_CONFIG, 0x0F); // enable 2-byte CRC, PWR_UP, and PRIM_RX + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg |= 0x03; // PWR_UP, and PRIM_RX + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + //nr204_write_reg(REG_EN_RXADDR, 0x03) // Set RX Pipe 0 and 1 + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(2); + return status; +} + +uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + furi_hal_gpio_write(nrf24_CE_PIN, false); + nrf24_write_reg(handle, REG_STATUS, 0x30); + //status = nrf24_write_reg(handle, REG_CONFIG, 0x0E); // enable 2-byte CRC, PWR_UP + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg &= 0xfe; // disable PRIM_RX + cfg |= 0x02; // PWR_UP + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(2); + return status; +} + +void nrf24_configure( + FuriHalSpiBusHandle* handle, + uint8_t rate, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t channel, + bool noack, + bool disable_aa) { + assert(channel <= 125); + assert(rate == 1 || rate == 2); + if(rate == 2) + rate = 8; // 2Mbps + else + rate = 0; // 1Mbps + + nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF + nrf24_set_idle(handle); + nrf24_write_reg(handle, REG_STATUS, 0x70); // clear interrupts + if(disable_aa) + nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst + else + nrf24_write_reg(handle, REG_EN_AA, 0x1F); // Enable Shockburst + + nrf24_write_reg(handle, REG_DYNPD, 0x3F); // enable dynamic payload length on all pipes + if(noack) + nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack + else { + nrf24_write_reg(handle, REG_CONFIG, 0x0C); // 2 byte CRC + nrf24_write_reg(handle, REG_FEATURE, 0x07); // enable dyn payload and ack + nrf24_write_reg( + handle, REG_SETUP_RETR, 0x1f); // 15 retries for AA, 500us auto retransmit delay + } + + nrf24_set_idle(handle); + nrf24_flush_rx(handle); + nrf24_flush_tx(handle); + + if(maclen) nrf24_set_maclen(handle, maclen); + if(srcmac) nrf24_set_src_mac(handle, srcmac, maclen); + if(dstmac) nrf24_set_dst_mac(handle, dstmac, maclen); + + nrf24_write_reg(handle, REG_RF_CH, channel); + nrf24_write_reg(handle, REG_RF_SETUP, rate); + furi_delay_ms(200); +} + +void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate) { + //uint8_t preamble[] = {0x55, 0x00}; // little endian + uint8_t preamble[] = {0xAA, 0x00}; // little endian + //uint8_t preamble[] = {0x00, 0x55}; // little endian + //uint8_t preamble[] = {0x00, 0xAA}; // little endian + nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF + nrf24_write_reg(handle, REG_STATUS, 0x70); // clear interrupts + nrf24_write_reg(handle, REG_DYNPD, 0x0); // disable shockburst + nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst + nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack + nrf24_set_maclen(handle, 2); // shortest address + nrf24_set_src_mac(handle, preamble, 2); // set src mac to preamble bits to catch everything + nrf24_set_packetlen(handle, 32); // set max packet length + nrf24_set_idle(handle); + nrf24_flush_rx(handle); + nrf24_flush_tx(handle); + nrf24_write_reg(handle, REG_RF_CH, channel); + nrf24_write_reg(handle, REG_RF_SETUP, rate); + + // prime for RX, no checksum + nrf24_write_reg(handle, REG_CONFIG, 0x03); // PWR_UP and PRIM_RX, disable AA and CRC + furi_hal_gpio_write(nrf24_CE_PIN, true); + furi_delay_ms(100); +} + +void hexlify(uint8_t* in, uint8_t size, char* out) { + memset(out, 0, size * 2); + for(int i = 0; i < size; i++) + snprintf(out + strlen(out), sizeof(out + strlen(out)), "%02X", in[i]); +} + +uint64_t bytes_to_int64(uint8_t* bytes, uint8_t size, bool bigendian) { + uint64_t ret = 0; + for(int i = 0; i < size; i++) + if(bigendian) + ret |= bytes[i] << ((size - 1 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 8; i++) { + if(bigendian) + out[i] = (val >> ((7 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian) { + uint32_t ret = 0; + for(int i = 0; i < 4; i++) + if(bigendian) + ret |= bytes[i] << ((3 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 4; i++) { + if(bigendian) + out[i] = (val >> ((3 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint64_t bytes_to_int16(uint8_t* bytes, bool bigendian) { + uint16_t ret = 0; + for(int i = 0; i < 2; i++) + if(bigendian) + ret |= bytes[i] << ((1 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int16_to_bytes(uint16_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 2; i++) { + if(bigendian) + out[i] = (val >> ((1 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +// handle iffyness with preamble processing sometimes being a bit (literally) off +void alt_address_old(uint8_t* packet, uint8_t* altaddr) { + uint8_t macmess_hi_b[4]; + uint8_t macmess_lo_b[2]; + uint32_t macmess_hi; + uint16_t macmess_lo; + uint8_t preserved; + + // get first 6 bytes into 32-bit and 16-bit variables + memcpy(macmess_hi_b, packet, 4); + memcpy(macmess_lo_b, packet + 4, 2); + + macmess_hi = bytes_to_int32(macmess_hi_b, true); + + //preserve least 7 bits from hi that will be shifted down to lo + preserved = macmess_hi & 0x7f; + macmess_hi >>= 7; + + macmess_lo = bytes_to_int16(macmess_lo_b, true); + macmess_lo >>= 7; + macmess_lo = (preserved << 9) | macmess_lo; + int32_to_bytes(macmess_hi, macmess_hi_b, true); + int16_to_bytes(macmess_lo, macmess_lo_b, true); + memcpy(altaddr, &macmess_hi_b[1], 3); + memcpy(altaddr + 3, macmess_lo_b, 2); +} + +bool validate_address(uint8_t* addr) { + uint8_t bad[][3] = {{0x55, 0x55}, {0xAA, 0xAA}, {0x00, 0x00}, {0xFF, 0xFF}}; + for(int i = 0; i < 4; i++) + for(int j = 0; j < 2; j++) + if(!memcmp(addr + j * 2, bad[i], 2)) return false; + + return true; +} + +bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address) { + bool found = false; + uint8_t packet[32] = {0}; + uint8_t packetsize; + //char printit[65]; + uint8_t status = 0; + status = nrf24_rxpacket(handle, packet, &packetsize, true); + if(status & 0x40) { + if(validate_address(packet)) { + for(int i = 0; i < maclen; i++) address[i] = packet[maclen - 1 - i]; + + /* + alt_address(packet, packet); + + for(i = 0; i < maclen; i++) + address[i + 5] = packet[maclen - 1 - i]; + */ + + //memcpy(address, packet, maclen); + //hexlify(packet, packetsize, printit); + found = true; + } + } + + return found; +} + +uint8_t nrf24_find_channel( + FuriHalSpiBusHandle* handle, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t rate, + uint8_t min_channel, + uint8_t max_channel, + bool autoinit) { + uint8_t ping_packet[] = {0x0f, 0x0f, 0x0f, 0x0f}; // this can be anything, we just need an ack + uint8_t ch = max_channel + 1; // means fail + nrf24_configure(handle, rate, srcmac, dstmac, maclen, 2, false, false); + for(ch = min_channel; ch <= max_channel + 1; ch++) { + nrf24_write_reg(handle, REG_RF_CH, ch); + if(nrf24_txpacket(handle, ping_packet, 4, true)) break; + } + + if(autoinit) { + FURI_LOG_D("nrf24", "initializing radio for channel %d", ch); + nrf24_configure(handle, rate, srcmac, dstmac, maclen, ch, false, false); + return ch; + } + + return ch; +} + +uint8_t nrf24_set_mac(uint8_t mac_addr, uint8_t *mac, uint8_t mlen) +{ + uint8_t addr[5]; + for(int i = 0; i < mlen; i++) addr[i] = mac[mlen - i - 1]; + return nrf24_write_buf_reg(nrf24_HANDLE, mac_addr, addr, mlen); +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/lib/nrf24/nrf24.h b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/lib/nrf24/nrf24.h new file mode 100644 index 000000000..cd994dc40 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/lib/nrf24/nrf24.h @@ -0,0 +1,381 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define W_TX_PAYLOAD_NOACK 0xB0 +#define W_ACK_PAYLOAD 0xA8 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define RF24_NOP 0xFF + +#define REG_CONFIG 0x00 +#define REG_EN_AA 0x01 +#define REG_EN_RXADDR 0x02 +#define REG_SETUP_AW 0x03 +#define REG_SETUP_RETR 0x04 +#define REG_DYNPD 0x1C +#define REG_FEATURE 0x1D +#define REG_RF_SETUP 0x06 +#define REG_STATUS 0x07 +#define REG_RX_ADDR_P0 0x0A +#define REG_RX_ADDR_P1 0x0B +#define REG_RX_ADDR_P2 0x0C +#define REG_RX_ADDR_P3 0x0D +#define REG_RX_ADDR_P4 0x0E +#define REG_RX_ADDR_P5 0x0F +#define REG_RF_CH 0x05 +#define REG_TX_ADDR 0x10 +#define REG_FIFO_STATUS 0x17 + +#define RX_PW_P0 0x11 +#define RX_PW_P1 0x12 +#define RX_PW_P2 0x13 +#define RX_PW_P3 0x14 +#define RX_PW_P4 0x15 +#define RX_PW_P5 0x16 +#define RX_DR 0x40 +#define TX_DS 0x20 +#define MAX_RT 0x10 + +#define nrf24_TIMEOUT 500 +#define nrf24_CE_PIN &gpio_ext_pb2 +#define nrf24_HANDLE &furi_hal_spi_bus_handle_external + +/* Low level API */ + +/** Write device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param data - data to write + * + * @return device status + */ +uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data); + +/** Write buffer to device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param data - data to write + * @param size - size of data to write + * + * @return device status + */ +uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); + +/** Read device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param[out] data - pointer to data + * + * @return device status + */ +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); + +/** Power up the radio for operation + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle); + +/** Power down the radio + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle); + +/** Sets the radio to RX mode + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle); + +/** Sets the radio to TX mode + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle); + +/*=============================================================================================================*/ + +/* High level API */ + +/** Must call this before using any other nrf24 API + * + */ +void nrf24_init(); + +/** Must call this when we end using nrf24 device + * + */ +void nrf24_deinit(); + +/** Send flush rx command + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle); + +/** Send flush tx command + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle); + +/** Gets the RX packet length in data pipe 0 + * + * @param handle - pointer to FuriHalSpiHandle + * pipe - pipe index (0..5) + * @return packet length in data pipe 0 + */ +uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle, uint8_t pipe); + +/** Sets the RX packet length in data pipe 0 + * + * @param handle - pointer to FuriHalSpiHandle + * @param len - length to set + * + * @return device status + */ +uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len); + +/** Gets configured length of MAC address + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return MAC address length + */ +uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle); + +/** Sets configured length of MAC address + * + * @param handle - pointer to FuriHalSpiHandle + * @param maclen - length to set MAC address to, must be greater than 1 and less than 6 + * + * @return MAC address length + */ +uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen); + +/** Gets the current status flags from the STATUS register + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return status flags + */ +uint8_t nrf24_status(FuriHalSpiBusHandle* handle); + +/** Gets the current transfer rate + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return transfer rate in bps + */ +uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle); + +/** Sets the transfer rate + * + * @param handle - pointer to FuriHalSpiHandle + * @param rate - the transfer rate in bps + * + * @return device status + */ +uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate); + +/** Gets the current channel + * In nrf24, the channel number is multiplied times 1MHz and added to 2400MHz to get the frequency + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return channel + */ +uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle); + +/** Sets the channel + * + * @param handle - pointer to FuriHalSpiHandle + * @param frequency - the frequency in hertz + * + * @return device status + */ +uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan); + +/** Gets the source mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] mac - the source mac address + * + * @return device status + */ +uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); + +/** Sets the source mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param mac - the mac address to set + * @param size - the size of the mac address (2 to 5) + * + * @return device status + */ +uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); + +/** Gets the dest mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] mac - the source mac address + * + * @return device status + */ +uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); + +/** Sets the dest mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param mac - the mac address to set + * @param size - the size of the mac address (2 to 5) + * + * @return device status + */ +uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); + +/** Reads RX packet + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] packet - the packet contents + * @param[out] ret_packetsize - size of the received packet + * @param packet_size: >1 - size, 1 - packet length is determined by RX_PW_P0 register, 0 - it is determined by dynamic payload length command + * + * @return device status + */ +uint8_t + nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* ret_packetsize, uint8_t packet_size_flag); + +/** Sends TX packet + * + * @param handle - pointer to FuriHalSpiHandle + * @param packet - the packet contents + * @param size - packet size + * @param ack - boolean to determine whether an ACK is required for the packet or not + * + * @return device status + */ +uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack); + +/** Configure the radio + * This is not comprehensive, but covers a lot of the common configuration options that may be changed + * @param handle - pointer to FuriHalSpiHandle + * @param rate - transfer rate in Mbps (1 or 2) + * @param srcmac - source mac address + * @param dstmac - destination mac address + * @param maclen - length of mac address + * @param channel - channel to tune to + * @param noack - if true, disable auto-acknowledge + * @param disable_aa - if true, disable ShockBurst + * + */ +void nrf24_configure( + FuriHalSpiBusHandle* handle, + uint8_t rate, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t channel, + bool noack, + bool disable_aa); + +// Set mac address (MSB first), Return: Status +uint8_t nrf24_set_mac(uint8_t mac_addr, uint8_t *mac, uint8_t mlen); + +/** Configures the radio for "promiscuous mode" and primes it for rx + * This is not an actual mode of the nrf24, but this function exploits a few bugs in the chip that allows it to act as if it were. + * See http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html for details. + * @param handle - pointer to FuriHalSpiHandle + * @param channel - channel to tune to + * @param rate - transfer rate in Mbps (1 or 2) + */ +void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate); + +/** Listens for a packet and returns first possible address sniffed + * Call this only after calling nrf24_init_promisc_mode + * @param handle - pointer to FuriHalSpiHandle + * @param maclen - length of target mac address + * @param[out] addresses - sniffed address + * + * @return success + */ +bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address); + +/** Sends ping packet on each channel for designated tx mac looking for ack + * + * @param handle - pointer to FuriHalSpiHandle + * @param srcmac - source address + * @param dstmac - destination address + * @param maclen - length of address + * @param rate - transfer rate in Mbps (1 or 2) + * @param min_channel - channel to start with + * @param max_channel - channel to end at + * @param autoinit - if true, automatically configure radio for this channel + * + * @return channel that the address is listening on, if this value is above the max_channel param, it failed + */ +uint8_t nrf24_find_channel( + FuriHalSpiBusHandle* handle, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t rate, + uint8_t min_channel, + uint8_t max_channel, + bool autoinit); + +/** Converts 64 bit value into uint8_t array + * @param val - 64-bit integer + * @param[out] out - bytes out + * @param bigendian - if true, convert as big endian, otherwise little endian + */ +void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian); + +/** Converts 32 bit value into uint8_t array + * @param val - 32-bit integer + * @param[out] out - bytes out + * @param bigendian - if true, convert as big endian, otherwise little endian + */ +void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian); + +/** Converts uint8_t array into 32 bit value + * @param bytes - uint8_t array + * @param bigendian - if true, convert as big endian, otherwise little endian + * + * @return 32-bit value + */ +uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/nrf24_packet_decoder.py b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/nrf24_packet_decoder.py new file mode 100644 index 000000000..d0ebc9c24 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/nrf24_packet_decoder.py @@ -0,0 +1,131 @@ +# +# NRF24L01+ Enhanced ShockBurst packets decoder +# +payload_len_default = 4 +packets = \ +( + '10101010 11101110 00000011 00001000 00001011 01000111 000100 10 0 10101010 10101010 10101010 10101010 00011101', + '10101010 11001000 11001000 11000011 110011 10 0 00001011 00000011 00000101 00000000 0010001100100000', + '10101010 11001000 11001000 11000100 000100 11 1 00001011 00000011 00000101 00000000 0010010011100010', + '10101010 11001000 11001000 11000100 00001011 00000011 00000101 00000010 1000010101000010', + '10101010 11001000 11001000 11000000 110011 10 0 11110101 00000010 00000011 00000000 0000111001000000', + '01010101 01000000 01101000 00010101 000000 00 0 0100100000100000', +# '01010101 01000010 11100100 10100110 01010101 01000100 110011 00 0 10010101 10110011 01100100 10101100 10101011 01010010 01111100 01001010 1100110100110001', + +) + +def bin2hex(x): + def bin2hex_helper(r): + while r: + yield r[0:2].upper() + r = r[2:] + if len(x) == 0: return + fmt = "{0:0" + str(int(len(x) / 8 * 2)) + "X}" + hex_data = fmt.format(int(x, 2)) + return list(bin2hex_helper(hex_data)) + +def bin2hexlong(b): + b = b.replace(" ", "") + out = ""; + n = 8 + for i in range(0, len(b), n): + b2 = b[i:i+n] + out = out + "{0:02X}".format(int(b2.ljust(8, '0'),2)) + return out + + +def split_packet(packet, parts): + """Split a string of 1s and 0s into multiple substrings as specified by parts. + Example: "111000011000", (3, 4, 2) -> ["111", "0000", "11", "000"] + :param packet: String of 1s and 0s + :param parts: Tuple of length of substrings + :return: list of substrings + """ + out = [] + packet = packet.replace(' ', '') + for part_length in parts: + out.append(packet[0:part_length]) + packet = packet[part_length:] + out.append(packet) + return out + + +def parse_packet(packet, address_length, ESB): + """Split a packet into its fields and return them as tuple.""" + if ESB: + preamble, address, payload_length, pid, no_ack, rest = split_packet(packet=packet, parts=(8, 8 * address_length, 6, 2, 1)) + payload, crc = split_packet(packet=rest, parts=((payload_len_default if int(payload_length, 2) > 32 else int(payload_length, 2)) * 8,)) + else: + preamble, address, rest = split_packet(packet=packet, parts=(8, 8 * address_length)) + crc = packet.rsplit(' ', 1)[1] + payload = rest[0:len(rest) - len(crc)] + payload_length = pid = no_ack = '' + + assert preamble in ('10101010', '01010101') + assert len(crc) in (8, 16) + + return preamble, address, payload_length, pid, no_ack, payload, crc + + +def crc(bits, size=8): + """Calculate the crc value for the polynomial initialized with 0xFF/0xFFFF) + :param size: 8 or 16 bit crc + :param bits: String of 1s and 0s + :return: + :polynomial: 1 byte CRC - standard is 0x107 = 0b100000111 = x^8+x^2+x^1+1, result the same for 0x07 + :polynomial: 2 byte CRC - standard is 0x11021 = X^16+X^12+X^5+1, result the same for 0x1021 + """ + if size == 8: + polynomial = 0x107 + crc = 0xFF + else: + polynomial = 0x11021 + crc = 0xFFFF + max_crc_value = (1 << size) - 1 # e.g. 0xFF for mode 8bit-crc + for bit in bits: + bit = int(bit, 2) + crc <<= 1 + if (crc >> size) ^ bit: # top most lfsr bit xor current data bit + crc ^= polynomial + crc &= max_crc_value # trim the crc to reject carry over bits +# print('{:X}'.format(crc)) + return crc + + +if __name__ == '__main__': + for packet in packets: + fld = packet.split(' '); + address_length = -1 + ESB = True + for f in fld: + if len(f) == 6 : break + if len(f) == 0 : + ESB = False + break + address_length += 1 + preamble, address, payload_length, pid, no_ack, payload, crc_received = \ + parse_packet(packet=packet, address_length=address_length, ESB=ESB) + crc_size = len(crc_received) + crc_received = '0x' + '{:X}'.format(int(crc_received, 2)) + print(f"Packet: {packet}") + print('\n'.join(( + f'Hex: {bin2hexlong(packet)}', + 'Preamble: 0x%X' % int(preamble,2), + f'Address: {address_length} bytes - {bin2hex(address)}'))) + if ESB: + print('\n'.join(( + f'Payload length in packet: {int(payload_length, 2)}, used: {(payload_len_default if int(payload_length, 2) > 32 else int(payload_length, 2))}', + f'Payload: {bin2hex(payload)}', + f'Pid: {int(pid, 2)}', + f'No_ack: {int(no_ack, 2) == 1}'))) + else: + print(f'Not Enhanced ShockBurst packet, payload length: {int(len(payload) / 8)}') + print(f'Payload: {bin2hex(payload)}') + print(f'CRC{crc_size}: {crc_received}') + crc_calculated = '0x' + '{:X}'.format(crc(address + payload_length + pid + no_ack + payload, size=crc_size)) + if crc_received == crc_calculated: + print('CRC is valid!') + else: + print(f'CRC mismatch! Calculated CRC is {crc_calculated}.') + print('-------------') + diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/nrf24scan.c b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/nrf24scan.c new file mode 100644 index 000000000..ede3e3f48 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/nrf24scan.c @@ -0,0 +1,1387 @@ +// +// Written by vad7, 20.11.2022. +// +#include "nrf24scan.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG "nrf24scan" +#define VERSION "2.1" +#define MAX_CHANNEL 125 +#define MAX_ADDR 6 + +#define SCAN_APP_PATH_FOLDER "/ext/nrf24scan" +#define SETTINGS_FILENAME "addresses.txt"// Settings file format (1 parameter per line): + // SNIFF - if present then sniff mode + // Rate: 0/1/2 - rate in Mbps (=0.25/1/2) + // Ch: 0..125 - default channel + // ESB: 0/1 (1 - Enhanced ShockBurst) + // DPL: 0/1 (1 - Dynamic Payload Length) + // CRC: 0/1/2 (CRC length) + // Payload: 1..32 (bytes) + // P0: address P0 in hex (5 byte, LSB last) + // P1: address P1 in hex (5 byte, LSB last) + // P2: address P2, LSB in hex (1 byte) + // P3: address P3, LSB in hex (1 byte) + // P4: address P4, LSB in hex (1 byte) + // P5: address P5, LSB in hex (1 byte) + // captured data: + // first byte = { RAW packet flag (0x80/0x00) } + { channel number } + // second byte = { Payload len 5 bits, 0 = 32 } + {{ RAW packet: ESB flag 0x04/0x00 + address size-2 if RAW packet } or { pipe #(0..5) }}, + // ... up to MAX_LOG_RECORDS-1 +#define SNIFF_FILENAME "sniff.txt" // settings for sniff mode +#define LOG_FILENAME "log" +#define LOG_FILEEXT ".txt" +#define MAX_LOG_RECORDS 200 +#define MAX_FOUND_RECORDS 70 +#define LOG_REC_SIZE 34 // max packet size +#define VIEW_LOG_MAX_X 22 +#define VIEW_LOG_WIDTH_B 10 // bytes + +const char SettingsFld_Rate[] = "Rate:"; +const char SettingsFld_Ch[] = "Ch:"; +const char SettingsFld_ESB[] = "ESB:"; +const char SettingsFld_DPL[] = "DPL:"; +const char SettingsFld_CRC[] = "CRC:"; +const char SettingsFld_Payload[] = "Payload:"; +const char SettingsFld_Sniff[] = "SNIFF"; +char SettingsFld_Addr = 'P'; + +Nrf24Scan* APP; +uint8_t what_doing = 0; // 0 - setup, 1 - view log, 2 - view addresses +uint8_t what_to_do = 1; // 0 - view, 1 - view & sniff, 2 - view & read, 3 - view & read selected addr +uint8_t save_settings = 0; +char screen_buf[64]; +char addr_file_name[32]; +uint8_t NRF_rate = 2; // 0 - 250Kbps, 1 - 1Mbps, 2 - 2Mbps +uint8_t NRF_channel = 0;// 0..125 +uint8_t NRF_ESB = 1; // 0 - ShockBurst, 1 - Enhanced ShockBurst +uint8_t NRF_DPL = 0; // 1 - Dynamic Payload Length +uint8_t NRF_CRC = 2; // 1 - No, 1 - CRC 1byte, 2 - CRC 2byte +uint8_t NRF_Payload = 32;// Payload len in bytes or Minimum payload in sniff mode, 0..32 +uint8_t NRF_Payload_sniff_min = 0; +uint8_t NRF_AA_OFF = 0; // Disable Auto Acknowledgement +bool NRF_ERROR = 0; + +struct ADDRS { + uint8_t addr_P0[5]; // MSB first + uint8_t addr_P1[5]; // MSB first + uint8_t addr_P2; // LSB only, MSB bytes equal addr_P1 + uint8_t addr_P3; // LSB only, MSB bytes equal addr_P1 + uint8_t addr_P4; // LSB only, MSB bytes equal addr_P1 + uint8_t addr_P5; // LSB only, MSB bytes equal addr_P1 + uint8_t addr_len; // 2..5 + uint8_t addr_count; +}; + +struct ADDRS addrs; +struct ADDRS addrs_sniff; +bool sniff_loaded = 0; +int16_t found_total; +int16_t view_found; + +int8_t log_to_file = 0; // 0 - no, 1 - yes(new), 2 - append, -1 - only clear +uint16_t log_arr_idx; +uint16_t view_log_arr_idx = 0; +uint16_t view_log_arr_x = 0; +bool save_to_new_log = true; +uint16_t last_packet_send = -1; +uint8_t last_packet_send_st = 0; +int16_t find_channel_period = 0; // sec +uint8_t menu_selected = 0; +uint32_t start_time; +uint8_t view_log_decode_PCF = 0; // view log: 1 - decode packet control field (9b) when ESB off. After pipe # (hex): +uint8_t view_log_decode_CRC = 0; // CRC bytes - 1/2, 0 - none + +#define menu_selected_max 5 +enum { + Menu_open_file = 0, + Menu_enter_channel, + Menu_enter_rate, + Menu_enter_scan_period, + Menu_log, + Menu_ok +}; + +//#define MIN(a, b) ((a> 7)); + arr++; + out += 2; + } while(--bytes); +} + +void clear_log() +{ + log_arr_idx = 0; + view_log_arr_idx = 0; + last_packet_send = -1; + found_total = 0; + view_found = -1; +} + +void write_to_log_file(Storage* storage, bool f_settings) +{ + if(log_arr_idx == 0 && !f_settings) return; + Stream* file_stream = file_stream_alloc(storage); + FuriString* str = furi_string_alloc(); + furi_string_set(str, SCAN_APP_PATH_FOLDER); + furi_string_cat(str, "/"); + bool fl; + if(f_settings) { + furi_string_cat(str, SETTINGS_FILENAME); + fl = file_stream_open(file_stream, furi_string_get_cstr(str), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS); + if(!fl) file_stream_close(file_stream); + } else { + furi_string_cat(str, LOG_FILENAME); + furi_string_cat(str, LOG_FILEEXT); + if(save_to_new_log) { + int cnt = 1; + do { + fl = file_stream_open(file_stream, furi_string_get_cstr(str), FSAM_READ_WRITE, FSOM_CREATE_NEW); + if(fl) break; + file_stream_close(file_stream); + furi_string_set(str, SCAN_APP_PATH_FOLDER); + furi_string_cat(str, "/"); + furi_string_cat(str, LOG_FILENAME); + furi_string_cat_printf(str, "-%02d", cnt); + furi_string_cat(str, LOG_FILEEXT); + } while(++cnt < 100); + if(!fl) { + FURI_LOG_E(TAG, "Failed to create new log file"); + notification_message(APP->notification, &sequence_blink_red_100); + } + } else { + fl = file_stream_open(file_stream, furi_string_get_cstr(str), FSAM_READ_WRITE, FSOM_OPEN_APPEND); + if(fl) { + if(stream_size(file_stream) == 0) save_to_new_log = true; + } else file_stream_close(file_stream); + } + } + if(fl) { + FURI_LOG_D(TAG, "Save to %s", furi_string_get_cstr(str)); + if(save_to_new_log || f_settings) { + if(what_to_do == 1) furi_string_printf(str, "%s\n", SettingsFld_Sniff); else furi_string_reset(str); + furi_string_cat_printf(str, "%s %d\n%s %d\n%s %d\n", SettingsFld_Rate, NRF_rate, SettingsFld_Ch, NRF_channel, SettingsFld_ESB, NRF_ESB); + furi_string_cat_printf(str, "%s %d\n%s %d\n%s %d\n", SettingsFld_DPL, NRF_DPL, SettingsFld_CRC, NRF_CRC, SettingsFld_Payload, what_to_do == 1 ? NRF_Payload_sniff_min : NRF_Payload); + if(addrs.addr_count > 0) { furi_string_cat_printf(str, "P0: "); add_to_furi_str_hex_bytes(str, (char*)addrs.addr_P0, addrs.addr_len); furi_string_cat(str, "\n"); } + if(addrs.addr_count > 1) { furi_string_cat_printf(str, "P1: "); add_to_furi_str_hex_bytes(str, (char*)addrs.addr_P1, addrs.addr_len); furi_string_cat(str, "\n"); } + if(addrs.addr_count > 2) { furi_string_cat_printf(str, "P2: "); furi_string_cat_printf(str, "%02X\n", addrs.addr_P2); } + if(addrs.addr_count > 3) { furi_string_cat_printf(str, "P3: "); furi_string_cat_printf(str, "%02X\n", addrs.addr_P3); } + if(addrs.addr_count > 4) { furi_string_cat_printf(str, "P4: "); furi_string_cat_printf(str, "%02X\n", addrs.addr_P4); } + if(addrs.addr_count > 5) { furi_string_cat_printf(str, "P5: "); furi_string_cat_printf(str, "%02X\n", addrs.addr_P5); } + if(!(fl = stream_write_string(file_stream, str) == furi_string_size(str))) { + FURI_LOG_E(TAG, "Failed to write header to file!"); + notification_message(APP->notification, &sequence_blink_red_100); + } + } + if(fl) { + if(f_settings) { + save_settings = 0; + if(strcmp(addr_file_name, "NONE") == 0) strcpy(addr_file_name, SETTINGS_FILENAME); + } else { + int i = 0; + for(; i < log_arr_idx; i++) { + furi_string_reset(str); + uint8_t *ptr = APP->log_arr + i * LOG_REC_SIZE; + int len; + if(ptr[0] & 0x80) { // RAW + len = (ptr[1] & 0b11)+2 + ((ptr[1] & 0b100) ? 2 : 0) + (ptr[1] >> 3) + 2; // addr + PCF? + payload + crcmax + } else { + len = (ptr[1] >> 3); + if(len == 0) len = 32; + } + //if(len < NRF_Payload) len = NRF_Payload; + add_to_furi_str_hex_bytes(str, (char*)ptr, len + 2); + furi_string_cat(str, "\n"); + if(stream_write_string(file_stream, str) != furi_string_size(str)) { + FURI_LOG_E(TAG, "Failed to write to file!"); + break; + } + } + if(i == log_arr_idx) { + notification_message(APP->notification, &sequence_blink_yellow_100); + FURI_LOG_D(TAG, "File saved"); + } + save_to_new_log = false; + } + } + file_stream_close(file_stream); + } else { + FURI_LOG_E(TAG, "Failed to open file %s", furi_string_get_cstr(str)); + notification_message(APP->notification, &sequence_blink_red_100); + } + stream_free(file_stream); + furi_string_free(str); +} + +static bool select_settings_file(Stream* stream) { + DialogsApp* dialogs = furi_record_open("dialogs"); + bool result = false; + FuriString* path; + path = furi_string_alloc(); + furi_string_set(path, SCAN_APP_PATH_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".txt", NULL); + browser_options.hide_ext = false; + + bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options); + + furi_record_close("dialogs"); + if(ret) { + if(!file_stream_open(stream, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_D(TAG, "Cannot open file \"%s\"", furi_string_get_cstr(path)); + file_stream_close(stream); + } else { + FURI_LOG_D(TAG, "Open file \"%s\"", furi_string_get_cstr(path)); + strncpy(addr_file_name, furi_string_get_cstr(path) + sizeof(SCAN_APP_PATH_FOLDER), sizeof(addr_file_name)); + result = true; + } + } + furi_string_free(path); + return result; +} + +// 0 - success, otherwise an error +static uint8_t load_settings_file(Stream* file_stream) { + size_t file_size = 0; + char* file_buf; + uint8_t err = 5; + file_size = stream_size(file_stream); + if(file_size == (size_t)0) { + FURI_LOG_D(TAG, "load failed. file_size: %d", file_size); + return 1; + } + file_size = MIN(file_size, (size_t) LOG_REC_SIZE * MAX_LOG_RECORDS * 2 + 100); + file_buf = malloc(file_size + 1); + if(file_buf == NULL) { + FURI_LOG_D(TAG, "Memory low, need: %d", file_size); + return 2; + } + memset(file_buf, 0, file_size + 1); + if(stream_read(file_stream, (uint8_t*)file_buf, file_size) == file_size) { + FURI_LOG_D(TAG, "Loading settings file"); + char* line_ptr = file_buf; + int16_t line_num = 0; + what_to_do = 2; + sniff_loaded = 0; + bool log_loaded = false; + while(line_ptr && (size_t)(line_ptr - file_buf) < file_size) { + char* end_ptr = strstr((char*)line_ptr, "\n"); + if(end_ptr == NULL) end_ptr = file_buf + file_size; else *end_ptr = '\0'; + int line_len = end_ptr - line_ptr; + if(*line_ptr == '\r' || line_len == 0) { + line_ptr = end_ptr + 1; + continue; + } + if(*(end_ptr - 1) < '0') { + *(end_ptr - 1) = '\0'; + line_len--; + } + //FURI_LOG_D(TAG, " L#%d: [%d]%s", line_num, line_len, line_ptr); + if(strncmp(line_ptr, SettingsFld_Rate, sizeof(SettingsFld_Rate)-1) == 0) { + NRF_rate = atoi(line_ptr + sizeof(SettingsFld_Rate)); + } else if(strncmp(line_ptr, SettingsFld_Ch, sizeof(SettingsFld_Ch)-1) == 0) { + NRF_channel = atoi(line_ptr + sizeof(SettingsFld_Ch)); + } else if(strncmp(line_ptr, SettingsFld_ESB, sizeof(SettingsFld_ESB)-1) == 0) { + NRF_ESB = atoi(line_ptr + sizeof(SettingsFld_ESB)); + } else if(strncmp(line_ptr, SettingsFld_DPL, sizeof(SettingsFld_DPL)-1) == 0) { + NRF_DPL = atoi(line_ptr + sizeof(SettingsFld_DPL)); + } else if(strncmp(line_ptr, SettingsFld_CRC, sizeof(SettingsFld_CRC)-1) == 0) { + NRF_CRC = atoi(line_ptr + sizeof(SettingsFld_CRC)); + if(what_to_do == 1) view_log_decode_CRC = NRF_CRC; + } else if(strncmp(line_ptr, SettingsFld_Payload, sizeof(SettingsFld_Payload)-1) == 0) { + uint8_t pld = atoi(line_ptr + sizeof(SettingsFld_Payload)); + if(pld > 32) pld = 32; + if(sniff_loaded) { + NRF_Payload_sniff_min = pld; + } else { + if(pld == 0) pld = 32; + NRF_Payload = pld; + } + } else if(strncmp(line_ptr, SettingsFld_Sniff, sizeof(SettingsFld_Sniff)-1) == 0) { + what_to_do = 1; + sniff_loaded = 1; + } else if(*line_ptr == SettingsFld_Addr) { + char a = *(++line_ptr); + struct ADDRS *adr = sniff_loaded ? &addrs_sniff : &addrs; + line_ptr += 3; + switch(a) { + case '0': + memset(adr, 0, sizeof(addrs)); + adr->addr_len = ConvertHexToArray(line_ptr, adr->addr_P0, sniff_loaded ? 3 : 5); + if(adr->addr_len >= 2) err = 0; + break; + case '1': + ConvertHexToArray(line_ptr, adr->addr_P1, what_to_do == 1 ? 3 : 5); + break; + case '2': + ConvertHexToArray(line_ptr, &adr->addr_P2, 1); + break; + case '3': + ConvertHexToArray(line_ptr, &adr->addr_P3, 1); + break; + case '4': + ConvertHexToArray(line_ptr, &adr->addr_P4, 1); + break; + case '5': + ConvertHexToArray(line_ptr, &adr->addr_P5, 1); + break; + default: + a = 0; + break; + } + if(err == 0 && a) adr->addr_count = a - '0' + 1; + } else if(line_len >= 3 * 2) { // data + if(!log_loaded) { + log_loaded = true; + clear_log(); + what_to_do = 0; + } + if(log_arr_idx < MAX_LOG_RECORDS - 1) { + if(ConvertHexToArray(line_ptr, APP->log_arr + log_arr_idx * LOG_REC_SIZE, LOG_REC_SIZE) > 0) err = 0; + log_arr_idx++; + } + } + line_ptr = end_ptr + 1; + line_num++; + } + } else { + FURI_LOG_D(TAG, "load failed. file size: %d", file_size); + err = 4; + } + free(file_buf); + return err; +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +void check_add_addr(uint8_t *pkt) +{ + if(addrs.addr_count > 0 && memcmp(addrs.addr_P0, pkt, addrs.addr_len) == 0) return; + if(addrs.addr_count > 1 && memcmp(addrs.addr_P1, pkt, addrs.addr_len - 1) == 0) { + if(addrs.addr_P1[addrs.addr_len - 1] == pkt[addrs.addr_len - 1]) return; + if(addrs.addr_count > 2 && addrs.addr_P2 == pkt[addrs.addr_len - 1]) return; + if(addrs.addr_count > 3 && addrs.addr_P3 == pkt[addrs.addr_len - 1]) return; + if(addrs.addr_count > 4 && addrs.addr_P4 == pkt[addrs.addr_len - 1]) return; + if(addrs.addr_count > 5 && addrs.addr_P5 == pkt[addrs.addr_len - 1]) return; + } + if(addrs.addr_count == 1) memcpy(addrs.addr_P1, pkt, addrs.addr_len); + else if(addrs.addr_count == 2) addrs.addr_P2 = pkt[addrs.addr_len - 1]; + else if(addrs.addr_count == 3) addrs.addr_P3 = pkt[addrs.addr_len - 1]; + else if(addrs.addr_count == 4) addrs.addr_P4 = pkt[addrs.addr_len - 1]; + else if(addrs.addr_count == 5) addrs.addr_P5 = pkt[addrs.addr_len - 1]; + addrs.addr_count++; +} + +static void prepare_nrf24(bool fsend_packet) +{ + nrf24_write_reg(nrf24_HANDLE, REG_STATUS, 0x70); // clear interrupts + nrf24_write_reg(nrf24_HANDLE, REG_RF_SETUP, NRF_rate); + uint8_t erx_addr = (1<<0); // Enable RX_P0 + struct ADDRS *adr = what_to_do == 1 ? &addrs_sniff : &addrs; + if(!fsend_packet) { + uint8_t payload = NRF_Payload; + uint8_t *rec = APP->log_arr + view_log_arr_idx * LOG_REC_SIZE; + uint8_t addr_size = (*(rec + 1) & 0b11) + 2; + bool setup_from_log = false; + if(what_to_do >= 2) { + if(log_arr_idx && (*rec & 0x80)) { + setup_from_log = true; + memcpy(addrs.addr_P0, rec + 2, addr_size); + addrs.addr_count = 1; + addrs.addr_len = addr_size; + payload = *(rec + 1) >> 3; + if(what_to_do == 2) { + uint8_t *p = APP->log_arr + 2; + int16_t i = 0; + for(i = 0; i < log_arr_idx; i++, p += LOG_REC_SIZE) { + if((*(p - 2) & 0x80) && (*(p - 1) & 0b11) + 2 == addr_size && rec + 2 != p) { + if(memcmp(p, addrs.addr_P0, addr_size - 1) == 0) { + check_add_addr(p); + if(addrs.addr_count >= 6) break; + } + } + } + } + } + } + if(what_to_do == 1) { // SNIFF + payload = 32; + nrf24_write_reg(nrf24_HANDLE, REG_CONFIG, 0x70); // Mask all interrupts, NO CRC + nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, 0); // Automatic Retransmission + nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0); // Auto acknowledgement + nrf24_write_reg(nrf24_HANDLE, REG_FEATURE, 0); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload + nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, NRF_channel); + } else if(setup_from_log) { // Scan + nrf24_write_reg(nrf24_HANDLE, REG_CONFIG, 0x70 | ((NRF_CRC == 1 ? 0b1000 : NRF_CRC == 2 ? 0b1100 : 0))); // Mask all interrupts + nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, *rec & 0x7F); + nrf24_write_reg(nrf24_HANDLE, REG_FEATURE, *(rec + 2 + addr_size) >> 2 != 0x33 ? 4+1 : 1); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload + if(*(rec + 1) & 0b100) { // ESB + nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, 0x01); // Automatic Retransmission + nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0x3F); // Auto acknowledgement + } else { + nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, 0); // Automatic Retransmission + nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0); // Auto acknowledgement + } + } else { + nrf24_write_reg(nrf24_HANDLE, REG_CONFIG, 0x70 | ((NRF_CRC == 1 ? 0b1000 : NRF_CRC == 2 ? 0b1100 : 0))); // Mask all interrupts + nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, NRF_ESB ? 0x01 : 0); // Automatic Retransmission + nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, NRF_AA_OFF || !NRF_ESB ? 0 : 0x3F); // Auto acknowledgement + nrf24_write_reg(nrf24_HANDLE, REG_FEATURE, NRF_DPL ? 4+1 : 1); // Enables the W_TX_PAYLOAD_NOACK command, Disable Payload with ACK, set Dynamic Payload + nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, NRF_DPL ? 0x3F : 0); // Enable dynamic payload reg + nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, NRF_channel); + } + if(adr->addr_count == 0) return; + nrf24_write_reg(nrf24_HANDLE, RX_PW_P0, payload); + nrf24_set_maclen(nrf24_HANDLE, adr->addr_len); + nrf24_set_mac(REG_RX_ADDR_P0, adr->addr_P0, adr->addr_len); + uint8_t tmp[5] = { 0 }; + nrf24_read_reg(nrf24_HANDLE, REG_RX_ADDR_P0, tmp, adr->addr_len); + for(uint8_t i = 0; i < adr->addr_len / 2; i++) { + uint8_t tb = tmp[i]; + tmp[i] = tmp[adr->addr_len - i - 1]; + tmp[adr->addr_len - i - 1] = tb; + } + NRF_ERROR = memcmp(adr->addr_P0, tmp, adr->addr_len) != 0; + FURI_LOG_D(TAG, "Payload: %d", payload); + nrf24_write_reg(nrf24_HANDLE, RX_PW_P0, payload); + if(adr->addr_count > 1) { + nrf24_set_mac(REG_RX_ADDR_P1, adr->addr_P1, adr->addr_len); + nrf24_write_reg(nrf24_HANDLE, RX_PW_P1, payload); + erx_addr |= (1<<1); // Enable RX_P1 + } else nrf24_write_reg(nrf24_HANDLE, RX_PW_P1, 0); + if(adr->addr_count > 2) { + nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P2, &adr->addr_P2, 1); + nrf24_write_reg(nrf24_HANDLE, RX_PW_P2, payload); + erx_addr |= (1<<2); // Enable RX_P2 + } else nrf24_write_reg(nrf24_HANDLE, RX_PW_P2, 0); + if(adr->addr_count > 3) { + nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P3, &adr->addr_P3, 1); + nrf24_write_reg(nrf24_HANDLE, RX_PW_P3, payload); + erx_addr |= (1<<3); // Enable RX_P3 + } else nrf24_write_reg(nrf24_HANDLE, RX_PW_P3, 0); + if(adr->addr_count > 4) { + nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P4, &adr->addr_P4, 1); + nrf24_write_reg(nrf24_HANDLE, RX_PW_P4, payload); + erx_addr |= (1<<4); // Enable RX_P4 + } else nrf24_write_reg(nrf24_HANDLE, RX_PW_P4, 0); + if(adr->addr_count > 5) { + nrf24_write_buf_reg(nrf24_HANDLE, REG_RX_ADDR_P5, &adr->addr_P5, 1); + nrf24_write_reg(nrf24_HANDLE, RX_PW_P5, payload); + erx_addr |= (1<<5); // Enable RX_P5 + } else nrf24_write_reg(nrf24_HANDLE, RX_PW_P5, 0); + nrf24_write_reg(nrf24_HANDLE, REG_EN_RXADDR, erx_addr); + } + nrf24_flush_rx(nrf24_HANDLE); + nrf24_flush_tx(nrf24_HANDLE); + nrf24_set_idle(nrf24_HANDLE); +} + +void correct_NRF_Payload_sniff_min() +{ + uint8_t pld = 32 - 3 - (NRF_ESB ? 2 : 0) - NRF_CRC + (addrs_sniff.addr_len - 2); + if(NRF_Payload_sniff_min > pld) NRF_Payload_sniff_min = pld; +} + +static void start_scanning() +{ + FURI_LOG_D(TAG, "Start proc-%d: Ch=%d Rate=%d", what_to_do, NRF_channel, NRF_rate); + if(what_to_do == 1) { // SNIFF + correct_NRF_Payload_sniff_min(); + view_log_decode_CRC = NRF_CRC; + } else if(sniff_loaded) { // Switch from sniff to scan/view + // to do... + } + prepare_nrf24(false); + if(NRF_ERROR) { + FURI_LOG_E(TAG, "NRF R/W ERROR!"); + return; + } + nrf24_set_rx_mode(nrf24_HANDLE); + start_time = furi_get_tick(); +} + +// start bitnum = 7 +uint32_t calc_crc(uint32_t crc, uint8_t *ptr, uint8_t bitnum, uint16_t bits) +{ + //uint8_t bitnum = 7; + uint32_t crc_high, polynom; + if(view_log_decode_CRC == 2) { + crc_high = (1<<16); + polynom = 0x1021; // X^16+X^12+X^5+1 => 0x11021 & 0xFFFF = 0x1021 + } else { + crc_high = (1<<8); + polynom = 0x07; // x^8+x^2+x^1+1 => 0x107 & 0xFF = 0x07 + } + while(bits--) { + crc <<= 1; + if(((crc & crc_high) != 0) ^ ((*ptr >> bitnum) & 1)) crc ^= polynom; + if(bitnum == 0) { + ptr++; + bitnum = 7; + } else bitnum--; + } + return crc & (view_log_decode_CRC == 2 ? 0xFFFF : 0xFF); +} + +// shifted 1 bit right +uint32_t get_shifted_crc(uint8_t *pcrc) +{ + uint32_t crc = ((*pcrc << 1) & 0xFF) | (*(pcrc+1) >> 7); + if(view_log_decode_CRC == 2) { + crc = (crc << 8) | ((*(pcrc+1) << 1) & 0xFF) | (*(pcrc+2) >> 7); + } + return crc; +} + +bool check_packet(uint8_t *pkt, uint16_t size) +{ + if(size < 3 || size > 32) return false; + uint8_t b = *pkt; + if(b == 0x55 || b == 0xAA || b == 0x00 || b == 0xFF) return false; // skip pkt when address begin with + uint32_t prevcrc = 0xFFFFFFFF; + bool found = false; + uint8_t addr_size = 3; + for(; addr_size <= 5; addr_size++) { + if(NRF_ESB){ + uint8_t _payload = *(pkt + addr_size) >> 2; + if((_payload > size - addr_size - 2 - view_log_decode_CRC && _payload != 0x33)) continue; + uint8_t *p = pkt + addr_size; + if(prevcrc == 0xFFFFFFFF) { + prevcrc = calc_crc(view_log_decode_CRC == 2 ? 0xFFFF : 0xFF, pkt, 7, 3 * 8); // crc for smallest addr + } + uint32_t crc; + if(addr_size > 3) crc = calc_crc(prevcrc, p - (addr_size - 3), 7, 8 * (addr_size - 3)); else crc = prevcrc; + if(_payload != 0x33) { // DPL + crc = calc_crc(crc, p, 7, 9 + _payload * 8); + if(crc == get_shifted_crc(p + _payload + 1)) { + *(pkt - 1) = ((_payload & 0x1F) << 3) + 0b100 + (addr_size - 2); + FURI_LOG_D(TAG, "VALID CRC %X: dpl: %d, addr: %d", (uint16_t)crc, _payload, addr_size); + found = true; + break; + } + } else { + crc = calc_crc(crc, p++, 7, 9); // PCF + if(crc == get_shifted_crc(p)) { + _payload = 0; + found = true; + } else { + for(uint8_t i = 1; i < size - addr_size - view_log_decode_CRC; i++) { + crc = calc_crc(crc, p++, 6, 8); + if(crc == get_shifted_crc(p)) { + _payload = i; + found = true; + break; + } + } + } + if(found) { + *(pkt - 1) = ((_payload & 0x1F) << 3) + 0b100 + (addr_size - 2); + FURI_LOG_D(TAG, "VALID CRC %X: pl: %d, addr: %d", (uint16_t)crc, _payload, addr_size); + break; + } + } + } else { + uint8_t *p; + if(addr_size == 3) { + prevcrc = calc_crc(view_log_decode_CRC == 2 ? 0xFFFF : 0xFF, pkt, 7, 3 * 8); // crc for smallest addr + p = pkt + addr_size; + } else { + p = pkt + addr_size - 1; + prevcrc = calc_crc(prevcrc, p++, 7, 8); + } + uint32_t crc = prevcrc; + if((view_log_decode_CRC == 1 && crc == *p) || (view_log_decode_CRC == 2 && crc == (uint32_t)((*p<<8) | *(p+1)))) { + *(pkt - 1) = ((0 & 0x1F) << 3) + 0b000 + (addr_size - 2); + FURI_LOG_D(TAG, "VALID CRC %X: pl: %d, addr: %d", (uint16_t)crc, 0, addr_size); + found = true; + break; + } + for(uint8_t i = 1; i <= size - addr_size - view_log_decode_CRC; i++) { + crc = calc_crc(crc, p++, 7, 8); + if((view_log_decode_CRC == 1 && crc == *p) || (view_log_decode_CRC == 2 && crc == (uint32_t)((*p<<8) | *(p+1)))) { + *(pkt - 1) = ((i & 0x1F) << 3) + 0b000 + (addr_size - 2); + FURI_LOG_D(TAG, "VALID CRC %X: pl: %d, addr: %d", (uint16_t)crc, i, addr_size); + found = true; + break; + } + } + if(found) break; + } + } + if(found) { + if(furi_log_get_level() == FuriLogLevelDebug) { + char dbuf[65]; + dbuf[0] = 0; + add_to_str_hex_bytes(dbuf, (char*)pkt, size); + FURI_LOG_D(TAG, "PKT%02X: %s (%d)", *(pkt - 1), dbuf, size); + } + int16_t i = 0; + for(; i < found_total; i++) { + if(APP->found[i].addr_size != addr_size) continue; + if(memcmp(APP->found[i].addr, pkt, addr_size) == 0) break; + } + if(i != found_total) { // found + APP->found[i].total++; + } else { + uint8_t *p = APP->log_arr + 2; + for(i = 0; i < log_arr_idx; i++, p += LOG_REC_SIZE) { + if((*(p - 2) & 0x80) && (*(p - 1) & 0b11) + 2 == addr_size && pkt != p) { + if(memcmp(p, pkt, addr_size) == 0) break; + } + } + if(i != log_arr_idx && found_total < MAX_FOUND_RECORDS) { // found -> 2 + memset(&APP->found[found_total], 0, sizeof(struct FOUND)); + memcpy(APP->found[found_total].addr, pkt, addr_size); + APP->found[found_total].addr_size = addr_size; + APP->found[found_total].total = 2; + if(found_total == 0) view_found = 0; + found_total++; + } + } + } + + return found; +} + +bool nrf24_read_newpacket() { + if(APP->log_arr == NULL) return false; + bool found = false; + uint8_t packetsize; + uint8_t *ptr = APP->log_arr + log_arr_idx * LOG_REC_SIZE; + uint8_t st; +/* test pkts + static int iii = 0; + char ppp[][65] = { "54A545109411544BAAE50110A3282512A9A1152A565B22AAA48AB751A5", + "C8C8C0CE7A81018007202FFFFC", + "EAEC8C8C2CE3C0101006FB737A", + "BEBFFFEC8C8C1CC00542AF7CFF7DBEAFE3397FEAFEF1DDFA4AEF7FDBB7CDEABC", + "FEAAAABEAAFEAAC8C8C28E1C810080490ABAF7FEEB76B7FDFEF7DFFB47FB97FE", + "A8AAC8C8C1CE20163DF7DFFD00", + "AFFEEFEC8C8C2CE4001010062F037F9BFFDF1DAD5EDBEF55DD9AB535FCB67F55", + "AC8C8C1CE5F8102000D503D7ABF", + "EE03080B4712555555550E80", + "C8C8C41385818280127100", + "AAC8C8C3CE05818280119000" + "AC8C8C413858182801271000", + "AAC8C8C40B0305028542" + }; + if(iii < 13) { + ConvertHexToArray(ppp[iii], ptr + 2, 32); + st = RX_DR; + packetsize = 32; + iii++; + } else +//*/ + st = nrf24_rxpacket(nrf24_HANDLE, ptr + 2 + (what_to_do == 1 ? addrs_sniff.addr_len - 2 : 0), &packetsize, what_to_do == 1 ? 32 : !NRF_DPL); + if(st & RX_DR) { + st = (st >> 1) & 7; // pipe # + if(what_to_do == 1) { // SNIFF + *ptr++ = NRF_channel | 0x80; + *ptr++ = st; // pipe # + if(addrs_sniff.addr_len > 2) { + *ptr = st == 0 ? addrs_sniff.addr_P0[2] : st == 1 ? addrs_sniff.addr_P1[2] : st == 2 ? addrs_sniff.addr_P2 : st == 3 ? addrs_sniff.addr_P3 : st == 4 ? addrs_sniff.addr_P4 : addrs_sniff.addr_P5; + } + if(!check_packet(ptr, packetsize)) { + if(addrs_sniff.addr_len > 2) return false; // skip if mac MSB added to preamble + uint8_t shifted = 0; + uint8_t shift_max = (32 - 3 - NRF_Payload_sniff_min - NRF_CRC) * 8 - 1; + while(shifted++ < shift_max) { // Shift packet left by one bit if minimum payload fits + uint8_t i = 0; + for(; i < packetsize - 1; i++) ptr[i] = (ptr[i] << 1) | (ptr[i + 1] >> 7); + ptr[i] <<= 1; + if(check_packet(ptr, packetsize - (shifted >> 3) - 1)) goto x_valid; + } + return false; + } + } else { + *ptr++ = NRF_channel; + *ptr++ = ((packetsize & 0x1F) << 3) | st; // payload size + pipe # + } +x_valid: + if(packetsize < 32) memset(ptr + packetsize, 0, 32 - packetsize); + if(log_arr_idx < MAX_LOG_RECORDS - 1) { + log_arr_idx++; + } else { + if(log_to_file == 1 || log_to_file == 2) { + write_to_log_file(APP->storage, false); + clear_log(); + } else { + memmove(APP->log_arr, APP->log_arr + LOG_REC_SIZE, log_arr_idx * LOG_REC_SIZE); + } + } + FURI_LOG_D(TAG, "Found packet #%d pipe %d", log_arr_idx, st); + notification_message(APP->notification, &sequence_blink_white_100); + found = true; + } + return found; +} + +bool nrf24_send_packet() +{ + if(log_arr_idx == 0) return false; + prepare_nrf24(!what_to_do); + uint8_t *ptr = APP->log_arr + view_log_arr_idx * LOG_REC_SIZE; + nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, *ptr & 0x7F); + if(*ptr & 0x80) { // RAW packet + //uint8_t pktinfo = *(ptr + 1); + //nrf24_set_maclen(nrf24_HANDLE, (pktinfo & 0b11) + 2); + //if(pktinfo & 0b100) { // ESB + nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, 0); // No Automatic Retransmission + nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0); // No Auto acknowledgement + //} + //uint8_t alen = (*(ptr + 2) & 0b11) + 2; + uint8_t adr[2]; + adr[0] = ptr[2]; + adr[1] = ptr[3]; + nrf24_set_maclen(nrf24_HANDLE, 2); + nrf24_set_mac(REG_RX_ADDR_P0, adr, 2); + nrf24_set_mac(REG_TX_ADDR, adr, 2); + last_packet_send_st = nrf24_txpacket(nrf24_HANDLE, ptr + 2 + 2, 32 - 2, false); + } else { + nrf24_write_reg(nrf24_HANDLE, REG_SETUP_RETR, NRF_ESB ? 0x11 : 0); // Automatic Retransmission + nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, NRF_AA_OFF || !NRF_ESB ? 0 : 0x3F); // Auto acknowledgement + uint8_t *adr; + uint8_t a = *(ptr + 1) & 0b111; + if(a < 2) { + if(a == 0) adr = addrs.addr_P0; else adr = addrs.addr_P1; + nrf24_set_mac(REG_RX_ADDR_P0, adr, addrs.addr_len); + nrf24_set_mac(REG_TX_ADDR, adr, addrs.addr_len); + } else { + uint8_t buf[5]; + memcpy(buf, addrs.addr_P1, addrs.addr_len - 1); + buf[addrs.addr_len - 1] = a == 2 ? addrs.addr_P2 : a == 3 ? addrs.addr_P3 : a == 4 ? addrs.addr_P4 : addrs.addr_P5; + nrf24_set_mac(REG_RX_ADDR_P0, buf, addrs.addr_len); + nrf24_set_mac(REG_TX_ADDR, buf, addrs.addr_len); + } + a = *(ptr + 1) >> 3; + if(a == 0) a = 32; + nrf24_write_reg(nrf24_HANDLE, REG_CONFIG, 0x70 | ((NRF_CRC == 1 ? 0b1000 : NRF_CRC == 2 ? 0b1100 : 0))); // Mask all interrupts + nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, NRF_DPL ? 0x3F : 0); // Enable dynamic payload reg + last_packet_send_st = nrf24_txpacket(nrf24_HANDLE, ptr + 2, a, false); + } + last_packet_send = view_log_arr_idx; + notification_message(APP->notification, last_packet_send_st ? &sequence_blink_blue_100 : &sequence_blink_red_100); + if(what_to_do) start_scanning(); + return last_packet_send_st; +} + +static void render_callback(Canvas* const canvas, void* ctx) { + const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); + if(plugin_state == NULL) return; + //canvas_draw_frame(canvas, 0, 0, 128, 64); // border around the edge of the screen + if(what_doing == 0) { + canvas_set_font(canvas, FontSecondary); // 8x10 font, 6 lines + if(save_settings) snprintf(screen_buf, sizeof(screen_buf), "Save: %s", SETTINGS_FILENAME); // menu_selected = 0 + else snprintf(screen_buf, sizeof(screen_buf), "Load: %s", addr_file_name); + canvas_draw_str(canvas, 10, 10, screen_buf); + snprintf(screen_buf, sizeof(screen_buf), "Ch: %d", NRF_channel); // menu_selected = 1 + canvas_draw_str(canvas, 10, 20, screen_buf); + if(NRF_ESB) { + strcpy(screen_buf, "ESB"); + if(NRF_DPL) strcat(screen_buf, " DPL"); + canvas_draw_str(canvas, 78, 20, screen_buf); + } + if(NRF_AA_OFF) { canvas_draw_str(canvas, 61, 20, "AA"); canvas_draw_line(canvas, 60, 21, 72, 12); } + snprintf(screen_buf, sizeof(screen_buf), "Rate: %sbps", NRF_rate == 2 ? "2M" : NRF_rate == 1 ? "1M" : "250K"); // menu_selected = 2 + canvas_draw_str(canvas, 10, 30, screen_buf); + if(what_to_do == 1) snprintf(screen_buf, sizeof(screen_buf), "Min Payl: %d", NRF_Payload_sniff_min); + else if(what_to_do >= 2) { + uint8_t *p = APP->log_arr + view_log_arr_idx * LOG_REC_SIZE; + snprintf(screen_buf, sizeof(screen_buf), "Payload: %d", log_arr_idx && (*p & 0x80) ? *(p + 1) >> 3 : NRF_Payload); + } else snprintf(screen_buf, sizeof(screen_buf), "Payload: %d", NRF_Payload); + canvas_draw_str(canvas, 78, 30, screen_buf); + strcpy(screen_buf, "Next Ch time: "); // menu_selected = 3 + if(find_channel_period == 0) strcat(screen_buf, "off"); else snprintf(screen_buf + strlen(screen_buf), sizeof(screen_buf), "%d s", find_channel_period); + canvas_draw_str(canvas, 10, 40, screen_buf); + if(NRF_CRC == 1) canvas_draw_str(canvas, 99, 40, "CRC1"); + else if(NRF_CRC == 2) canvas_draw_str(canvas, 99, 40, "CRC2"); + snprintf(screen_buf, sizeof(screen_buf), "Log: %s", log_to_file == 0 ? "No" : log_to_file == 1 ? "Yes" : log_to_file == 2 ? "Append" : "Clear");// menu_selected = 4 + canvas_draw_str(canvas, 10, 50, screen_buf); + if(what_to_do) { // menu_selected = 5 + if(NRF_ERROR) snprintf(screen_buf, sizeof(screen_buf), "nRF24L01+ r/w ERROR!"); + else { + if(what_to_do == 1) snprintf(screen_buf, sizeof(screen_buf), "Start sniff"); + else { + uint8_t *p = APP->log_arr + view_log_arr_idx * LOG_REC_SIZE; + if(log_arr_idx && (*p & 0x80)) { // +RAW + snprintf(screen_buf, sizeof(screen_buf), "Start read: "); + add_to_str_hex_bytes(screen_buf, (char*)p + 2, (*(p + 1) & 0b11) + 2); + if(what_to_do == 2) strcpy(screen_buf + strlen(screen_buf) - 2, "* "); + } else snprintf(screen_buf, sizeof(screen_buf), "Start scan (pipes: %d)", addrs.addr_count); + } + } + } else snprintf(screen_buf, sizeof(screen_buf), "View log (pipes: %d)", addrs.addr_count); + canvas_draw_str(canvas, 10, 60, screen_buf); + canvas_draw_str(canvas, 0, menu_selected * 10 + 10, ">"); + } else if(what_doing == 1){ + canvas_set_font(canvas, FontBatteryPercent); // 5x7 font, 9 lines + bool ch2 = false; + screen_buf[0] = '\0'; + if(view_log_arr_x == 0) { strcat(screen_buf, " "); ch2 = true; + } else { + snprintf(screen_buf, sizeof(screen_buf), "<%d", view_log_arr_x); + if(view_log_arr_x < VIEW_LOG_MAX_X) ch2 = true; + } + snprintf(screen_buf + strlen(screen_buf), sizeof(screen_buf), " %s ch: %d - %d.", what_to_do == 1 ? "Sniff" : what_to_do == 0 ? "View" : "Read", NRF_channel, log_arr_idx); + canvas_draw_str(canvas, 0, 7, screen_buf); + if(ch2) canvas_draw_str(canvas, 121, 7, ">"); + if(log_arr_idx) { + if(view_log_arr_idx >= log_arr_idx) view_log_arr_idx = log_arr_idx - 1; + uint16_t page = view_log_arr_idx & ~7; + for(uint8_t i = 0; i < 8 && page + i < log_arr_idx; i++) { + screen_buf[0] = (view_log_arr_idx & 7) != i ? ' ' : last_packet_send != view_log_arr_idx ? '>' : last_packet_send_st ? '*' : '!'; + screen_buf[1] = '\0'; + uint8_t *ptr = APP->log_arr + (page + i) * LOG_REC_SIZE; + uint8_t channel = *ptr++; + uint8_t *crcptr = NULL; + uint8_t pre = 0; + int count = 0; + if(channel & 0x80) { // RAW packet: nn:>{.address..}-xxxxxxxx + uint8_t pktinfo = *ptr++; + bool _PCF = pktinfo & 0b100; + uint8_t plen = count = (pktinfo >> 3); + uint8_t adrsize = (pktinfo & 0b11) + 2; + plen += adrsize; + count += view_log_decode_CRC; + if(view_log_arr_x > 0) count -= view_log_arr_x - 1; + uint8_t max_width = VIEW_LOG_WIDTH_B; + if(view_log_arr_x == 0) max_width -= 5; + if(count > max_width) count = max_width; + if(count > 0) { + uint8_t *pcrc = ptr; + uint32_t crc; + crc = view_log_decode_CRC == 2 ? 0xFFFF : 0xFF; + crc = calc_crc(crc, pcrc, 7, (_PCF? 9 : 0) + plen * 8); + pcrc += plen; + if(_PCF) { //ESB + pcrc++; + if(crc == get_shifted_crc(pcrc)) crcptr = pcrc; + } else { + if((view_log_decode_CRC == 1 && crc == *pcrc) || (view_log_decode_CRC == 2 && crc == (uint32_t)((*pcrc<<8) | *(pcrc+1)))) { + crcptr = pcrc; + } + } + if(view_log_arr_x == 0) { + add_to_str_hex_bytes(screen_buf, (char*)ptr, adrsize); + for(int8_t j = 5 - adrsize; j > 0; j--) strcat(screen_buf, " "); + strcat(screen_buf, "-"); + pre += 5 * 2 + 1; + } else { + ptr += view_log_arr_x - 1; + } + ptr += adrsize; + if(_PCF) add_to_str_hex_bytes_shift_9b(screen_buf, (char*)ptr++, count); else add_to_str_hex_bytes(screen_buf, (char*)ptr, count); + } + } else { + uint8_t dpl = *ptr++; + uint8_t pipe = dpl & 0b111; + dpl >>= 3; + if(dpl == 0) dpl = 32; + count = dpl - view_log_arr_x; + if(view_log_decode_PCF) count--; + uint8_t max_width = VIEW_LOG_WIDTH_B; + if(view_log_arr_x == 0) { + if(addrs.addr_count > 1) max_width--; + if(view_log_decode_PCF) max_width -= 2; + } + if(count > max_width) count = max_width; + if(count > 0) { + if(view_log_decode_CRC) { + static uint16_t prev_addr_CRC; + static int8_t prev_pipe = -1; + uint8_t *pcrc = ptr; + uint32_t crc; + if(prev_pipe == pipe) { crc = prev_addr_CRC; + } else { + crc = view_log_decode_CRC == 2 ? 0xFFFF : 0xFF; + if(pipe <= 1) { crc = calc_crc(crc, pipe == 0 ? addrs.addr_P0 : addrs.addr_P1, 7, addrs.addr_len * 8); + } else { + crc = calc_crc(crc, addrs.addr_P1, 7, (addrs.addr_len - 1) * 8); + crc = calc_crc(crc, pipe == 2 ? &addrs.addr_P2 : pipe == 3 ? &addrs.addr_P3 : pipe == 4 ? &addrs.addr_P4 : &addrs.addr_P5, 7, 8); + } + prev_addr_CRC = crc; + prev_pipe = pipe; + } + if(view_log_decode_PCF) { + crc = calc_crc(crc, pcrc++, 7, 9); + if(crc == get_shifted_crc(pcrc)) crcptr = pcrc; + if(crcptr == NULL) { + for(int8_t j = 0; j < (int8_t)dpl - view_log_decode_CRC - 1; j++) { + crc = calc_crc(crc, pcrc++, 6, 8); + if(crc == get_shifted_crc(pcrc)) { + crcptr = pcrc; + break; + } + } + } + } else { + for(int8_t j = 0; j < (int8_t)dpl - view_log_decode_CRC; j++) { + crc = calc_crc(crc, pcrc++, 7, 8); + if((view_log_decode_CRC == 1 && crc == *pcrc) || (view_log_decode_CRC == 2 && crc == (uint32_t)((*pcrc<<8) | *(pcrc+1)))) { + crcptr = pcrc; + break; + } + } + } + } + } + ptr += view_log_arr_x; + if(max_width < VIEW_LOG_WIDTH_B) { + pre += snprintf(screen_buf + 1, 10, "%X-", pipe); + if(view_log_decode_PCF) { + pre += snprintf(screen_buf + strlen(screen_buf), 10, "%02X%01X-", *ptr >> 2, ((*ptr & 3) << 1) | (*(ptr+1) >> 7)); + } + } + if(view_log_decode_PCF) add_to_str_hex_bytes_shift_9b(screen_buf, (char*)ptr++, count); else add_to_str_hex_bytes(screen_buf, (char*)ptr, count); + } + uint16_t y = 14 + i * 7; + canvas_draw_str(canvas, 3 * 5, y, screen_buf); + uint16_t x = page + i + 1; + if(x > 99) { + snprintf(screen_buf, 16, "%d", x); + canvas_draw_str(canvas, 1, y, screen_buf + 1); + canvas_draw_frame(canvas, 0, y - 2, 1, 2); + x = 2; + } else { + x = snprintf(screen_buf, 16, "%d", x); + canvas_draw_str(canvas, 0, y, screen_buf); + } + if(crcptr) { // 5x7 font, 9 lines + canvas_draw_str(canvas, x * 5, y, "="); + int n = crcptr - (uint8_t*)ptr; + if(n > -view_log_decode_CRC && n < count) { + int len; + x = (4 + pre) * 5; + if(n < 0) { + len = view_log_decode_CRC + n; + n = 0; + } else { + len = MIN(view_log_decode_CRC, count - n); + x += n * 2 * 5; + canvas_draw_line(canvas, x - 1, y, x - 1, y - 1); + } + canvas_draw_line(canvas, x - 1, y, n = x + len * 2 * 5 - 1, y); + canvas_draw_line(canvas, n, y, n, y - 1); + } + } else canvas_draw_str(canvas, x * 5, y, ":"); + } + } + } else { + canvas_set_font(canvas, FontBatteryPercent); // 5x7 font, 9 lines + if(view_found >= 0) { + snprintf(screen_buf, 50, "Found > 1: %d", found_total); + canvas_draw_str(canvas, 0, 1 * 7, screen_buf); + int16_t idx = view_found * 7; + for(uint8_t i = 0; i < 7; i++, idx++) { + if(idx >= found_total) break; + snprintf(screen_buf, 16, "%d. ", idx + 1); + add_to_str_hex_bytes(screen_buf, (char*)APP->found[idx].addr, APP->found[idx].addr_size); + if(APP->found[idx].addr_size == 3) strcat(screen_buf, " "); else if(APP->found[idx].addr_size == 4) strcat(screen_buf, " "); + snprintf(screen_buf + strlen(screen_buf), 16, " - %d", APP->found[idx].total); + canvas_draw_str(canvas, 0, (2 + i) * 7, screen_buf); + } + } else { + struct ADDRS *a; + if(what_to_do == 1) { + a = &addrs_sniff; + canvas_draw_str(canvas, 0, 1 * 7, "Sniff prefix:"); + } else { + a = &addrs; + canvas_draw_str(canvas, 0, 1 * 7, "Addresses:"); + } + if(a->addr_count > 0) { + snprintf(screen_buf, sizeof(screen_buf), "P0: "); + add_to_str_hex_bytes(screen_buf, (char*)a->addr_P0, a->addr_len); + canvas_draw_str(canvas, 0, 2 * 7, screen_buf); + } + if(a->addr_count > 1) { + snprintf(screen_buf, sizeof(screen_buf), "P1: "); + add_to_str_hex_bytes(screen_buf, (char*)a->addr_P1, a->addr_len); + canvas_draw_str(canvas, 0, 3 * 7, screen_buf); + } + if(a->addr_count > 2) { + canvas_draw_str(canvas, 0, 4 * 7, "P2: "); + snprintf(screen_buf, sizeof(screen_buf), "%02X", a->addr_P2); + canvas_draw_str(canvas, (4 + (a->addr_len - 1) * 2) * 5, 4 * 7, screen_buf); + } + if(a->addr_count > 3) { + canvas_draw_str(canvas, 0, 5 * 7, "P3: "); + snprintf(screen_buf, sizeof(screen_buf), "%02X", a->addr_P3); + canvas_draw_str(canvas, (4 + (a->addr_len - 1) * 2) * 5, 5 * 7, screen_buf); + } + if(a->addr_count > 4) { + canvas_draw_str(canvas, 0, 6 * 7, "P4: "); + snprintf(screen_buf, sizeof(screen_buf), "%02X", a->addr_P4); + canvas_draw_str(canvas, (4 + (a->addr_len - 1) * 2) * 5, 6 * 7, screen_buf); + } + if(a->addr_count > 5) { + canvas_draw_str(canvas, 0, 7 * 7, "P5: "); + snprintf(screen_buf, sizeof(screen_buf), "%02X", a->addr_P5); + canvas_draw_str(canvas, (4 + (a->addr_len - 1) * 2) * 5, 7 * 7, screen_buf); + } + } + if(log_arr_idx) { + uint8_t *ptr = APP->log_arr + view_log_arr_idx * LOG_REC_SIZE; + uint8_t pktinfo = *(ptr + 1); + snprintf(screen_buf, 32, ">Ch: %d L: %d", *ptr & 0x7F, pktinfo >> 3); + if(*ptr & 0x80) { + strcat(screen_buf, " RAW"); + if(pktinfo & 0b100) { + snprintf(screen_buf + strlen(screen_buf), 16, " ESB %s", *(ptr + 2 + (pktinfo & 0b11) + 2) >> 2 != 0x33 ? "DPL" : ""); + } + } + canvas_draw_str(canvas, 0, 8 * 7, screen_buf); + } + screen_buf[0] = 'v'; + strcpy(screen_buf + 1, VERSION); + canvas_draw_str(canvas, 105, 7, screen_buf); + if(view_log_decode_PCF || view_log_decode_CRC) { + strcpy(screen_buf, "Decode: "); + if(view_log_decode_PCF) strcat(screen_buf, "ESB "); + if(view_log_decode_CRC == 1) strcat(screen_buf, "CRC1"); + else if(view_log_decode_CRC == 2) strcat(screen_buf, "CRC2"); + canvas_draw_str(canvas, 0, 64, screen_buf); + } + } + release_mutex((ValueMutex*)ctx, plugin_state); +} + +int32_t nrf24scan_app(void* p) { + UNUSED(p); + APP = malloc(sizeof(Nrf24Scan)); + APP->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + APP->plugin_state = malloc(sizeof(PluginState)); + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, APP->plugin_state, sizeof(PluginState))) { + furi_message_queue_free(APP->event_queue); + FURI_LOG_E(TAG, "cannot create mutex"); + free(APP->plugin_state); + return 255; + } + APP->log_arr = malloc(LOG_REC_SIZE * MAX_LOG_RECORDS); + if(APP->log_arr == NULL) { + FURI_LOG_E(TAG, "Not enouch memory: %d", LOG_REC_SIZE * MAX_LOG_RECORDS); + strcpy(addr_file_name, "MEMORY LOW!"); + } + clear_log(); + APP->found = malloc(sizeof(struct FOUND) * MAX_FOUND_RECORDS); + if(APP->found == NULL) { + FURI_LOG_E(TAG, "Not enouch memory: %d", sizeof(struct FOUND) * MAX_FOUND_RECORDS); + strcpy(addr_file_name, "MEMORY LOW!!"); + } + + memset((uint8_t*)&addrs, 0, sizeof(addrs)); + memset((uint8_t*)&addrs_sniff, 0, sizeof(addrs_sniff)); + nrf24_init(); + + // Set system callbacks + APP->view_port = view_port_alloc(); + view_port_draw_callback_set(APP->view_port, render_callback, &state_mutex); + view_port_input_callback_set(APP->view_port, input_callback, APP->event_queue); + + // Open GUI and register view_port + APP->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(APP->gui, APP->view_port, GuiLayerFullscreen); + APP->notification = furi_record_open(RECORD_NOTIFICATION); + APP->storage = furi_record_open(RECORD_STORAGE); + storage_common_mkdir(APP->storage, SCAN_APP_PATH_FOLDER); + Stream* file_stream = file_stream_alloc(APP->storage); + FuriString* path = furi_string_alloc(); + furi_string_set(path, SCAN_APP_PATH_FOLDER); + furi_string_cat(path, "/"); + furi_string_cat(path, SNIFF_FILENAME); + if(file_stream_open(file_stream, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint8_t err = load_settings_file(file_stream); + if(!err) strncpy(addr_file_name, furi_string_get_cstr(path) + sizeof(SCAN_APP_PATH_FOLDER), sizeof(addr_file_name)); + else snprintf(addr_file_name, sizeof(addr_file_name), "LOAD ERROR#%d", err); + } else { + strcpy(addr_file_name, "NONE"); + if(what_to_do == 1) { + addrs.addr_P0[0] = 0; + addrs.addr_P0[1] = 0x55; + addrs.addr_len = 2; + addrs.addr_count = 1; + view_log_decode_CRC = NRF_CRC = 2; + NRF_Payload_sniff_min = 0; // Min + } + } + file_stream_close(file_stream); + stream_free(file_stream); + furi_string_free(path); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(APP->event_queue, &event, 100); + PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + //FURI_LOG_D(TAG, "Key: %d Type: %d Sec: %u", event.input.key, event.input.type, event.input.sequence); + switch(event.input.key) { + case InputKeyUp: + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + if(what_doing == 0) { + if(menu_selected > 0) menu_selected--; else menu_selected = menu_selected_max; + } else if(what_doing == 1) { + view_log_arr_idx -= event.input.type == InputTypeRepeat ? 10 : 1; + if(view_log_arr_idx >= log_arr_idx) view_log_arr_idx = 0; + } else if(what_doing == 2) { + if(view_found > -1) view_found--; + } + } + break; + case InputKeyDown: + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + if(what_doing == 0) { + if(menu_selected < menu_selected_max) menu_selected++; else menu_selected = 0; + } else if(what_doing == 1) { + view_log_arr_idx += event.input.type == InputTypeRepeat ? 10 : 1; + if(view_log_arr_idx >= log_arr_idx) view_log_arr_idx = log_arr_idx - 1; + } else if(what_doing == 2) { + if(view_found < found_total / 7) view_found++; + } + } + break; + case InputKeyLeft: + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + if(what_doing == 0) { + switch(menu_selected) { + case Menu_enter_channel: + NRF_channel -= event.input.type == InputTypeRepeat ? 10 : 1; + if(NRF_channel > MAX_CHANNEL) NRF_channel = MAX_CHANNEL; + break; + case Menu_enter_rate: + if(what_to_do == 1) { // SNIFF + NRF_Payload_sniff_min -= event.input.type == InputTypeRepeat ? 10 : 1; + correct_NRF_Payload_sniff_min(); + } else { + NRF_Payload -= event.input.type == InputTypeRepeat ? 10 : 1; + if(NRF_Payload > 32) NRF_Payload = 0; + } + break; + case Menu_enter_scan_period: + find_channel_period -= event.input.type == InputTypeRepeat ? 10 : 1; + if(find_channel_period < 0) find_channel_period = 0; + break; + case Menu_log: + if(--log_to_file < -1) log_to_file = 2; + break; + case Menu_ok: + if(--what_to_do > 3) what_to_do = 3; + break; + } + } else if(what_doing == 1) { + if(view_log_arr_x > 0) view_log_arr_x--; + } else if(what_doing == 2) { + //if(NRF_ESB == 0) + view_log_decode_PCF ^= 1; + } + } + break; + case InputKeyRight: + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + if(what_doing == 0) { + switch(menu_selected) { + case Menu_open_file: + save_settings ^= 1; + break; + case Menu_enter_channel: + NRF_channel += event.input.type == InputTypeRepeat ? 10 : 1; + if(NRF_channel > MAX_CHANNEL) NRF_channel = 0; + break; + case Menu_enter_rate: + if(what_to_do == 1) { // SNIFF + NRF_Payload_sniff_min += event.input.type == InputTypeRepeat ? 10 : 1; + correct_NRF_Payload_sniff_min(); + } else { + NRF_Payload += event.input.type == InputTypeRepeat ? 10 : 1; + if(NRF_Payload > 32) NRF_Payload = 32; + } + break; + case Menu_enter_scan_period: + find_channel_period += event.input.type == InputTypeRepeat ? 10 : 1; + break; + case Menu_log: + if(++log_to_file > 2) log_to_file = -1; + break; + case Menu_ok: + if(++what_to_do > 3) what_to_do = 0; + break; + } + } else if(what_doing == 1) { + if(view_log_arr_x < VIEW_LOG_MAX_X) view_log_arr_x++; + } else if(what_doing == 2) { + if(++view_log_decode_CRC > 2) view_log_decode_CRC = 0; + } + } + break; + case InputKeyOk: + if(event.input.type == InputTypeShort) { + if(what_doing == 0) { + switch(menu_selected) { + case Menu_open_file: + if(save_settings) { + write_to_log_file(APP->storage, true); + } else { + file_stream = file_stream_alloc(APP->storage); + if(select_settings_file(file_stream)) { + uint8_t err = load_settings_file(file_stream); + if(!err) save_to_new_log = true; else snprintf(addr_file_name, sizeof(addr_file_name), "LOAD ERROR#%d", err); + file_stream_close(file_stream); + menu_selected = Menu_ok; + } + stream_free(file_stream); + } + break; + case Menu_enter_channel: + if(what_to_do == 1) { + if(NRF_ESB) NRF_DPL = NRF_ESB = 0; else NRF_ESB = 1; + } else { + if(NRF_ESB) { + if(NRF_DPL) NRF_DPL = NRF_ESB = 0; else NRF_DPL = 1; + } else NRF_ESB = 1; + } + break; + case Menu_enter_rate: + NRF_rate++; + if(NRF_rate > 2) NRF_rate = 0; + break; + case Menu_enter_scan_period: + if(++NRF_CRC > 2) NRF_CRC = what_to_do == 1 ? 1 : 0; + break; + case Menu_ok: + if(what_to_do) { + if((addrs.addr_count || (what_to_do >= 2 && log_arr_idx && *(APP->log_arr + view_log_arr_idx * LOG_REC_SIZE) & 0x80)) || what_to_do == 1) { + if(log_to_file == -1) { + log_to_file = 0; + clear_log(); + save_to_new_log = true; + } else if(log_to_file == 1) save_to_new_log = true; + start_scanning(); + if(!NRF_ERROR) what_doing = 1; + } + } else what_doing = 1; + break; + } + } else if(what_doing == 1) { + what_doing = 2; + } else if(what_doing == 2) { + what_doing = 1; + } + } else if(event.input.type == InputTypeLong) { + if(what_doing == 0) { + if(menu_selected == Menu_enter_channel) { + NRF_AA_OFF ^= 1; + } else if(menu_selected == Menu_log) { // Log + if(log_arr_idx && (log_to_file == 1 || log_to_file == 2)) { + write_to_log_file(APP->storage, false); + clear_log(); + } + } + } else if(what_doing == 1 || what_doing == 2) { + nrf24_send_packet(); + } + } + break; + case InputKeyBack: + if(event.input.type == InputTypeLong) processing = false; + else if(event.input.type == InputTypeShort) { + if(what_doing) what_doing--; + if(what_doing == 0) { + nrf24_set_idle(nrf24_HANDLE); + } + } + break; + default: + break; + } + } + } + if(what_doing && what_to_do) { + nrf24_read_newpacket(); + if(find_channel_period && furi_get_tick() - start_time >= (uint32_t)find_channel_period * 1000UL) { + if(++NRF_channel > MAX_CHANNEL) NRF_channel = 0; + start_scanning(); + } + } + + view_port_update(APP->view_port); + release_mutex(&state_mutex, plugin_state); + } + nrf24_set_idle(nrf24_HANDLE); + if(log_arr_idx && (log_to_file == 1 || log_to_file == 2)) { + write_to_log_file(APP->storage, false); + } + nrf24_deinit(); + + view_port_enabled_set(APP->view_port, false); + gui_remove_view_port(APP->gui, APP->view_port); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_STORAGE); + view_port_free(APP->view_port); + furi_message_queue_free(APP->event_queue); + free(APP->plugin_state); + if(APP->log_arr) free(APP->log_arr); + if(APP->found) free(APP->found); + free(APP); + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/nrf24scan.h b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/nrf24scan.h new file mode 100644 index 000000000..0fa7cbc63 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/nrf24scan.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + int x; + int y; +} PluginState; + +struct FOUND { + uint8_t addr_size; + uint8_t addr[5]; + uint16_t total; +}; + +typedef struct { + Gui* gui; + FuriMessageQueue* event_queue; + PluginState* plugin_state; + ViewPort* view_port; + Storage* storage; + NotificationApp* notification; + uint8_t* log_arr; + struct FOUND *found; +} Nrf24Scan; + diff --git a/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/nrf24scan_10px.png b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/nrf24scan_10px.png new file mode 100644 index 000000000..348b35eca Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/nrf24scan/nrf24scan_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ocarina/README.md b/Applications/Official/DEV_FW/source/xMasterX/ocarina/README.md new file mode 100644 index 000000000..1fcfd00fa --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ocarina/README.md @@ -0,0 +1,4 @@ +# flipperzero-ocarina +A basic Ocarina (of Time) for the Flipper Zero. + +Controls are the same as the N64 version of the Ocarina of Time, the Ok button takes the place of the A button diff --git a/Applications/Official/DEV_FW/source/xMasterX/ocarina/application.fam b/Applications/Official/DEV_FW/source/xMasterX/ocarina/application.fam new file mode 100644 index 000000000..16a786701 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ocarina/application.fam @@ -0,0 +1,13 @@ +App( + appid="Ocarina", + name="Ocarina", + apptype=FlipperAppType.EXTERNAL, + entry_point="ocarina_app", + cdefines=["APP_OCARINA"], + requires=["gui"], + stack_size=1 * 1024, + order=30, + fap_icon="icons/music_10px.png", + fap_category="Music_Extra", + fap_icon_assets="icons", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/ocarina/icons/music_10px.png b/Applications/Official/DEV_FW/source/xMasterX/ocarina/icons/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ocarina/icons/music_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ocarina/music_10px.png b/Applications/Official/DEV_FW/source/xMasterX/ocarina/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/ocarina/music_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/ocarina/ocarina.c b/Applications/Official/DEV_FW/source/xMasterX/ocarina/ocarina.c new file mode 100644 index 000000000..7fdfce74c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/ocarina/ocarina.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include + +#define NOTE_UP 587.33f +#define NOTE_LEFT 493.88f +#define NOTE_RIGHT 440.00f +#define NOTE_DOWN 349.23 +#define NOTE_OK 293.66f + +typedef struct { + FuriMutex* model_mutex; + + FuriMessageQueue* event_queue; + + ViewPort* view_port; + Gui* gui; +} Ocarina; + +void draw_callback(Canvas* canvas, void* ctx) { + Ocarina* ocarina = ctx; + furi_check(furi_mutex_acquire(ocarina->model_mutex, FuriWaitForever) == FuriStatusOk); + + //canvas_draw_box(canvas, ocarina->model->x, ocarina->model->y, 4, 4); + canvas_draw_frame(canvas, 0, 0, 128, 64); + canvas_draw_str(canvas, 50, 10, "Ocarina"); + canvas_draw_str(canvas, 30, 20, "OK button for A"); + + furi_mutex_release(ocarina->model_mutex); +} + +void input_callback(InputEvent* input, void* ctx) { + Ocarina* ocarina = ctx; + // Puts input onto event queue with priority 0, and waits until completion. + furi_message_queue_put(ocarina->event_queue, input, FuriWaitForever); +} + +Ocarina* ocarina_alloc() { + Ocarina* instance = malloc(sizeof(Ocarina)); + + instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + instance->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + instance->view_port = view_port_alloc(); + view_port_draw_callback_set(instance->view_port, draw_callback, instance); + view_port_input_callback_set(instance->view_port, input_callback, instance); + + instance->gui = furi_record_open("gui"); + gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); + + return instance; +} + +void ocarina_free(Ocarina* instance) { + view_port_enabled_set(instance->view_port, false); // Disabsles our ViewPort + gui_remove_view_port(instance->gui, instance->view_port); // Removes our ViewPort from the Gui + furi_record_close("gui"); // Closes the gui record + view_port_free(instance->view_port); // Frees memory allocated by view_port_alloc + furi_message_queue_free(instance->event_queue); + + furi_mutex_free(instance->model_mutex); + + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } + + free(instance); +} + +int32_t ocarina_app(void* p) { + UNUSED(p); + + Ocarina* ocarina = ocarina_alloc(); + + InputEvent event; + for(bool processing = true; processing;) { + // Pops a message off the queue and stores it in `event`. + // No message priority denoted by NULL, and 100 ticks of timeout. + FuriStatus status = furi_message_queue_get(ocarina->event_queue, &event, 100); + furi_check(furi_mutex_acquire(ocarina->model_mutex, FuriWaitForever) == FuriStatusOk); + + float volume = 1.0f; + if(status == FuriStatusOk) { + if(event.type == InputTypePress) { + switch(event.key) { + case InputKeyUp: + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + furi_hal_speaker_start(NOTE_UP, volume); + } + break; + case InputKeyDown: + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + furi_hal_speaker_start(NOTE_DOWN, volume); + } + break; + case InputKeyLeft: + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + furi_hal_speaker_start(NOTE_LEFT, volume); + } + break; + case InputKeyRight: + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + furi_hal_speaker_start(NOTE_RIGHT, volume); + } + break; + case InputKeyOk: + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + furi_hal_speaker_start(NOTE_OK, volume); + } + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } else if(event.type == InputTypeRelease) { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } + } + } + + furi_mutex_release(ocarina->model_mutex); + view_port_update(ocarina->view_port); // signals our draw callback + } + ocarina_free(ocarina); + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/paint/application.fam b/Applications/Official/DEV_FW/source/xMasterX/paint/application.fam new file mode 100644 index 000000000..ad7f322b0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/paint/application.fam @@ -0,0 +1,12 @@ +App( + appid="Paint", + name="Paint", + apptype=FlipperAppType.EXTERNAL, + entry_point="paint_app", + cdefines=["APP_PAINT"], + requires=["gui"], + stack_size=2 * 1024, + order=175, + fap_icon="paintIcon.png", + fap_category="Misc_Extra", +) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/paint/paint.c b/Applications/Official/DEV_FW/source/xMasterX/paint/paint.c new file mode 100644 index 000000000..5cfe85155 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/paint/paint.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include +#include +#include // Header-file for boolean data-type. + +typedef struct selected_position { + int x; + int y; +} selected_position; + +typedef struct { + selected_position selected; + bool board[32][16]; + bool isDrawing; +} PaintData; + +void paint_draw_callback(Canvas* canvas, void* ctx) { + const PaintData* paint_state = acquire_mutex((ValueMutex*)ctx, 25); + UNUSED(ctx); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + //draw the canvas(32x16) on screen(144x64) using 4x4 tiles + for(int y = 0; y < 16; y++) { + for(int x = 0; x < 32; x++) { + if(paint_state->board[x][y]) { + canvas_draw_box(canvas, x * 4, y * 4, 4, 4); + } + } + } + + //draw cursor as a 4x4 black box with a 2x2 white box inside + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, paint_state->selected.x * 4, paint_state->selected.y * 4, 4, 4); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box( + canvas, paint_state->selected.x * 4 + 1, paint_state->selected.y * 4 + 1, 2, 2); + + //release the mutex + release_mutex((ValueMutex*)ctx, paint_state); +} + +void paint_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t paint_app(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + PaintData* paint_state = malloc(sizeof(PaintData)); + ValueMutex paint_state_mutex; + if(!init_mutex(&paint_state_mutex, paint_state, sizeof(PaintData))) { + FURI_LOG_E("paint", "cannot create mutex\r\n"); + free(paint_state); + return -1; + } + + // Configure view port + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, paint_draw_callback, &paint_state_mutex); + view_port_input_callback_set(view_port, paint_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + //NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + InputEvent event; + + while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { + //break out of the loop if the back key is pressed + if(event.type == InputTypeShort && event.key == InputKeyBack) { + break; + } + + //check the key pressed and change x and y accordingly + if(event.type == InputTypeShort) { + switch(event.key) { + case InputKeyUp: + paint_state->selected.y -= 1; + break; + case InputKeyDown: + paint_state->selected.y += 1; + break; + case InputKeyLeft: + paint_state->selected.x -= 1; + break; + case InputKeyRight: + paint_state->selected.x += 1; + break; + case InputKeyOk: + paint_state->board[paint_state->selected.x][paint_state->selected.y] = + !paint_state->board[paint_state->selected.x][paint_state->selected.y]; + break; + + default: + break; + } + + //check if cursor position is out of bounds and reset it to the closest position + if(paint_state->selected.x < 0) { + paint_state->selected.x = 0; + } + if(paint_state->selected.x > 31) { + paint_state->selected.x = 31; + } + if(paint_state->selected.y < 0) { + paint_state->selected.y = 0; + } + if(paint_state->selected.y > 15) { + paint_state->selected.y = 15; + } + if(paint_state->isDrawing == true) { + paint_state->board[paint_state->selected.x][paint_state->selected.y] = true; + } + view_port_update(view_port); + } + if(event.key == InputKeyBack && event.type == InputTypeLong) { + paint_state->board[1][1] = true; + for(int y = 0; y < 16; y++) { + for(int x = 0; x < 32; x++) { + paint_state->board[x][y] = false; + } + } + view_port_update(view_port); + } + if(event.key == InputKeyOk && event.type == InputTypeLong) { + paint_state->isDrawing = !paint_state->isDrawing; + paint_state->board[paint_state->selected.x][paint_state->selected.y] = true; + view_port_update(view_port); + } + } + + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + free(paint_state); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/paint/paintIcon.png b/Applications/Official/DEV_FW/source/xMasterX/paint/paintIcon.png new file mode 100644 index 000000000..cc0a8b7d8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/paint/paintIcon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/LICENSE new file mode 100644 index 000000000..0e259d42c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/README.md b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/README.md new file mode 100644 index 000000000..e8e7491f5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/README.md @@ -0,0 +1,34 @@ +# flipperzero_pomodoro + +The Pomodoro Technique is a time management method developed by Francesco Cirillo in the late 1980s.[1] It uses a kitchen timer to break work into intervals, typically 25 minutes in length, separated by short breaks. Each interval is known as a pomodoro, from the Italian word for tomato, after the tomato-shaped kitchen timer Cirillo used as a university student. + +Flipper Zero is a portable Tamagotchi-like multi-functional device developed for interaction with access control systems. The device is able to read, copy, and emulate radio-frequency (RFID) tags, radio remotes, and digital access keys. + +## Pomodoro timer application for Flipper Zero + +Three timers available: + +- classic 25 min work, 5 min rest +- long 50 min work, 10 min rest +- sprint 10 min work, 2 min rest + +With tomato counter + +Plays sound alerts + +Has built-in clocks + +Screenshots: + +![](./misc/1.png) + +![](./misc/2.png) + +![](./misc/3.png) + +![](./misc/4.png) + +![](./misc/5.png) + + +Compatible with firmware v. F81999EA from 14 Oct. 2022 diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/application.fam b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/application.fam new file mode 100644 index 000000000..7a507a4fd --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/application.fam @@ -0,0 +1,15 @@ +App( + appid="Pomodoro_Timer", + name="Pomodoro Timer", + apptype=FlipperAppType.EXTERNAL, + entry_point="pomodoro_app", + stack_size=1 * 1024, + cdefines=["APP_POMODORO"], + requires=[ + "gui", + ], + order=10, + fap_icon="pomodoro_timer.png", + fap_category="Misc_Extra", + fap_icon_assets="icons", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/icons/ButtonLeft_4x7.png b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/icons/ButtonLeft_4x7.png new file mode 100644 index 000000000..0b4655d43 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/icons/ButtonLeft_4x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/icons/Ok_btn_9x9.png b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/icons/Ok_btn_9x9.png new file mode 100644 index 000000000..9a1539da2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/icons/Ok_btn_9x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/icons/Pin_back_arrow_10x8.png b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/icons/Pin_back_arrow_10x8.png new file mode 100644 index 000000000..3bafabd14 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/icons/Pin_back_arrow_10x8.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/icons/Space_65x18.png b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/icons/Space_65x18.png new file mode 100644 index 000000000..b60ae5097 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/icons/Space_65x18.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/1.png b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/1.png new file mode 100644 index 000000000..e8543a255 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/2.png b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/2.png new file mode 100644 index 000000000..8b5f28476 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/3.png b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/3.png new file mode 100644 index 000000000..32473be3c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/4.png b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/4.png new file mode 100644 index 000000000..0b48b9fdc Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/5.png b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/5.png new file mode 100644 index 000000000..1a53bc074 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/misc/5.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro.c b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro.c new file mode 100644 index 000000000..5b1db1984 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro.c @@ -0,0 +1,164 @@ +#include "pomodoro.h" +#include + +#define TAG "PomodoroApp" + +enum PomodoroDebugSubmenuIndex { + PomodoroSubmenuIndex10, + PomodoroSubmenuIndex25, + PomodoroSubmenuIndex50, +}; + +void pomodoro_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + Pomodoro* app = context; + if(index == PomodoroSubmenuIndex10) { + app->view_id = PomodoroView10; + view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroView10); + } + if(index == PomodoroSubmenuIndex25) { + app->view_id = PomodoroView25; + view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroView25); + } + if(index == PomodoroSubmenuIndex50) { + app->view_id = PomodoroView50; + view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroView50); + } +} + +void pomodoro_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + Pomodoro* app = context; + if(result == DialogExResultLeft) { + view_dispatcher_stop(app->view_dispatcher); + } else if(result == DialogExResultRight) { + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view + } else if(result == DialogExResultCenter) { + view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroViewSubmenu); + } +} + +uint32_t pomodoro_exit_confirm_view(void* context) { + UNUSED(context); + return PomodoroViewExitConfirm; +} + +uint32_t pomodoro_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +Pomodoro* pomodoro_app_alloc() { + Pomodoro* app = malloc(sizeof(Pomodoro)); + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // Notifications + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Submenu view + app->submenu = submenu_alloc(); + submenu_add_item( + app->submenu, + "Classic: 25 work 5 rest", + PomodoroSubmenuIndex25, + pomodoro_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Long: 50 work 10 rest", + PomodoroSubmenuIndex50, + pomodoro_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Sprint: 10 work 2 rest", + PomodoroSubmenuIndex10, + pomodoro_submenu_callback, + app); + view_set_previous_callback(submenu_get_view(app->submenu), pomodoro_exit); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroViewSubmenu, submenu_get_view(app->submenu)); + + // Dialog view + app->dialog = dialog_ex_alloc(); + dialog_ex_set_result_callback(app->dialog, pomodoro_dialog_callback); + dialog_ex_set_context(app->dialog, app); + dialog_ex_set_left_button_text(app->dialog, "Exit"); + dialog_ex_set_right_button_text(app->dialog, "Stay"); + dialog_ex_set_center_button_text(app->dialog, "Menu"); + dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroViewExitConfirm, dialog_ex_get_view(app->dialog)); + + // 25 minutes view + app->pomodoro_25 = pomodoro_25_alloc(); + view_set_previous_callback(pomodoro_25_get_view(app->pomodoro_25), pomodoro_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroView25, pomodoro_25_get_view(app->pomodoro_25)); + + // 50 minutes view + app->pomodoro_50 = pomodoro_50_alloc(); + view_set_previous_callback(pomodoro_50_get_view(app->pomodoro_50), pomodoro_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroView50, pomodoro_50_get_view(app->pomodoro_50)); + + // 10 minutes view + app->pomodoro_10 = pomodoro_10_alloc(); + view_set_previous_callback(pomodoro_10_get_view(app->pomodoro_10), pomodoro_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, PomodoroView10, pomodoro_10_get_view(app->pomodoro_10)); + + // TODO switch to menu after Media is done + app->view_id = PomodoroViewSubmenu; + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + + return app; +} + +void pomodoro_app_free(Pomodoro* app) { + furi_assert(app); + + // Reset notification + notification_internal_message(app->notifications, &sequence_reset_blue); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, PomodoroViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, PomodoroViewExitConfirm); + dialog_ex_free(app->dialog); + view_dispatcher_remove_view(app->view_dispatcher, PomodoroView25); + pomodoro_25_free(app->pomodoro_25); + view_dispatcher_remove_view(app->view_dispatcher, PomodoroView50); + pomodoro_50_free(app->pomodoro_50); + view_dispatcher_remove_view(app->view_dispatcher, PomodoroView10); + pomodoro_10_free(app->pomodoro_10); + view_dispatcher_free(app->view_dispatcher); + + // Close records + furi_record_close(RECORD_GUI); + app->gui = NULL; + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Free rest + free(app); +} + +int32_t pomodoro_app(void* p) { + UNUSED(p); + // Switch profile to Hid + Pomodoro* app = pomodoro_app_alloc(); + + view_dispatcher_run(app->view_dispatcher); + + pomodoro_app_free(app); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro.h b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro.h new file mode 100644 index 000000000..53dedb8e3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include "pomodoro_timer.h" +#include "views/pomodoro_10.h" +#include "views/pomodoro_25.h" +#include "views/pomodoro_50.h" + +typedef struct { + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + DialogEx* dialog; + PomodoroTimer* pomodoro_10; + PomodoroTimer* pomodoro_25; + PomodoroTimer* pomodoro_50; + uint32_t view_id; +} Pomodoro; + +typedef enum { + PomodoroViewSubmenu, + PomodoroView10, + PomodoroView25, + PomodoroView50, + PomodoroViewExitConfirm, +} PomodoroView; diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro_timer.c b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro_timer.c new file mode 100644 index 000000000..0fba5db42 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro_timer.c @@ -0,0 +1,242 @@ +#include "pomodoro_timer.h" +#include +#include +#include +#include +#include + +const NotificationSequence sequence_finish = { + &message_display_backlight_on, + &message_green_255, + &message_vibro_on, + &message_note_c5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_e5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_g5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_b5, + &message_delay_250, + &message_vibro_off, + &message_vibro_on, + &message_note_c6, + &message_delay_250, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_rest = { + &message_display_backlight_on, + &message_red_255, + &message_vibro_on, + &message_note_c6, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_b5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_g5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_e5, + &message_delay_100, + &message_vibro_off, + &message_vibro_on, + &message_note_c5, + &message_delay_250, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +void pomodoro_timer_process(PomodoroTimer* pomodoro_timer, InputEvent* event) { + with_view_model( + pomodoro_timer->view, + PomodoroTimerModel * model, + { + if(event->type == InputTypePress) { + if(event->key == InputKeyOk) { + model->ok_pressed = true; + } else if(event->key == InputKeyLeft) { + model->reset_pressed = true; + } else if(event->key == InputKeyBack) { + model->back_pressed = true; + } + } else if(event->type == InputTypeRelease) { + if(event->key == InputKeyOk) { + model->ok_pressed = false; + + // START/STOP TIMER + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t current_timestamp = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + // STARTED -> PAUSED + if(model->timer_running) { + // Update stopped seconds + model->timer_stopped_seconds = + current_timestamp - model->timer_start_timestamp; + } else if(!model->time_passed) { + // INITIAL -> STARTED + model->timer_start_timestamp = current_timestamp; + model->rest_running = false; + } else { + // PAUSED -> STARTED + model->timer_start_timestamp = + current_timestamp - model->timer_stopped_seconds; + } + model->timer_running = !model->timer_running; + } else if(event->key == InputKeyLeft) { + if(!model->timer_running) { + furi_record_close(RECORD_NOTIFICATION); + model->timer_stopped_seconds = 0; + model->timer_start_timestamp = 0; + model->time_passed = 0; + model->timer_running = false; + } + model->reset_pressed = false; + } else if(event->key == InputKeyBack) { + model->back_pressed = false; + } + } + }, + true); +} + +void pomodoro_draw_callback(Canvas* canvas, void* context, int max_seconds, int max_seconds_rest) { + furi_assert(context); + PomodoroTimerModel* model = context; + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t current_timestamp = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + // Header + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Pomodoro"); + + canvas_draw_icon(canvas, 68, 1, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 127, 1, AlignRight, AlignTop, "Hold to exit"); + + // Start/Pause/Continue + int txt_main_y = 34; + canvas_draw_icon(canvas, 63, 23, &I_Space_65x18); // button + if(model->ok_pressed) { + elements_slightly_rounded_box(canvas, 66, 25, 60, 13); + canvas_set_color(canvas, ColorWhite); + } + if(model->timer_running) { + model->time_passed = current_timestamp - model->timer_start_timestamp; + elements_multiline_text_aligned(canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Pause"); + canvas_draw_box(canvas, 71, 27, 2, 8); + canvas_draw_box(canvas, 75, 27, 2, 8); + } else { + if(model->time_passed) { + elements_multiline_text_aligned( + canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Continue"); + } else { + elements_multiline_text_aligned( + canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Start"); + } + canvas_draw_icon(canvas, 70, 26, &I_Ok_btn_9x9); // OK icon + } + canvas_set_color(canvas, ColorBlack); + + // Reset + if(!model->timer_running && model->time_passed) { + canvas_draw_icon(canvas, 63, 46, &I_Space_65x18); + if(model->reset_pressed) { + elements_slightly_rounded_box(canvas, 66, 48, 60, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 72, 50, &I_ButtonLeft_4x7); + elements_multiline_text_aligned(canvas, 83, 57, AlignLeft, AlignBottom, "Reset"); + canvas_set_color(canvas, ColorBlack); + } + + char buffer[64]; + + // Time to work + int total_time_left = (max_seconds - (uint32_t)model->time_passed); + int minutes_left = total_time_left / 60; + int seconds_left = total_time_left % 60; + canvas_set_font(canvas, FontBigNumbers); + + // Play sound + if(total_time_left == 0 && !model->sound_playing) { + model->sound_playing = true; + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_finish); + } + if(total_time_left < 0) { + model->timer_running = false; + model->time_passed = 0; + model->sound_playing = false; + + model->rest_running = true; + model->rest_start_timestamp = current_timestamp; + seconds_left = 0; + model->counter += 1; + } + if(!model->rest_running) { + snprintf(buffer, sizeof(buffer), "%02d:%02d", minutes_left, seconds_left); + canvas_draw_str(canvas, 0, 39, buffer); + } + if(model->timer_running) { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 0, 50, AlignLeft, AlignTop, "Time to work"); + } + + // Time to rest + if(model->rest_running && !model->timer_running) { + canvas_set_font(canvas, FontBigNumbers); + int rest_passed = current_timestamp - model->rest_start_timestamp; + int rest_total_time_left = (max_seconds_rest - rest_passed); + int rest_minutes_left = rest_total_time_left / 60; + int rest_seconds_left = rest_total_time_left % 60; + + // Play sound + if(rest_total_time_left == 0 && !model->sound_playing) { + model->sound_playing = true; + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_rest); + } + if(rest_total_time_left < 0) { + rest_seconds_left = 0; + model->rest_running = false; + model->sound_playing = false; + } + snprintf(buffer, sizeof(buffer), "%02d:%02d", rest_minutes_left, rest_seconds_left); + canvas_draw_str(canvas, 0, 60, buffer); + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 0, 27, AlignLeft, AlignTop, "Have a rest"); + } + + // Clocks + canvas_set_font(canvas, FontSecondary); + snprintf( + buffer, + sizeof(buffer), + "%02ld:%02ld:%02ld", + ((uint32_t)current_timestamp % (60 * 60 * 24)) / (60 * 60), + ((uint32_t)current_timestamp % (60 * 60)) / 60, + (uint32_t)current_timestamp % 60); + canvas_draw_str(canvas, 0, 20, buffer); + + // Tomato counter + if(model->counter > 5) { + model->counter = 1; + } + for(int i = 0; i < model->counter; ++i) { + canvas_draw_disc(canvas, 122 - i * 10, 15, 4); + } +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro_timer.h b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro_timer.h new file mode 100644 index 000000000..284f0a6c6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro_timer.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include + +typedef struct PomodoroTimer PomodoroTimer; + +struct PomodoroTimer { + View* view; +}; + +typedef struct PomodoroTimerModel PomodoroTimerModel; + +struct PomodoroTimerModel { + bool ok_pressed; + bool reset_pressed; + bool back_pressed; + bool connected; + bool timer_running; + bool rest_running; + bool sound_playing; + uint32_t timer_start_timestamp; + uint32_t timer_stopped_seconds; + uint32_t time_passed; + uint32_t rest_start_timestamp; + int counter; +}; + +void pomodoro_timer_process(PomodoroTimer* pomodoro_timer, InputEvent* event); + +void pomodoro_draw_callback(Canvas* canvas, void* context, int max_seconds, int max_seconds_rest); diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro_timer.png b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro_timer.png new file mode 100644 index 000000000..b25c2718e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/pomodoro_timer.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_10.c b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_10.c new file mode 100644 index 000000000..f11f96d9f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_10.c @@ -0,0 +1,46 @@ +#include "../pomodoro_timer.h" +#include "pomodoro_10.h" +#include +#include +#include +#include + +static void pomodoro_10_draw_callback(Canvas* canvas, void* context) { + int max_seconds = 60 * 10; + int max_seconds_rest = 60 * 2; + pomodoro_draw_callback(canvas, context, max_seconds, max_seconds_rest); +} + +static bool pomodoro_10_input_callback(InputEvent* event, void* context) { + furi_assert(context); + PomodoroTimer* pomodoro_10 = context; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + return false; + } else { + pomodoro_timer_process(pomodoro_10, event); + return true; + } +} + +PomodoroTimer* pomodoro_10_alloc() { + PomodoroTimer* pomodoro_10 = malloc(sizeof(PomodoroTimer)); + pomodoro_10->view = view_alloc(); + view_set_context(pomodoro_10->view, pomodoro_10); + view_allocate_model(pomodoro_10->view, ViewModelTypeLocking, sizeof(PomodoroTimerModel)); + view_set_draw_callback(pomodoro_10->view, pomodoro_10_draw_callback); + view_set_input_callback(pomodoro_10->view, pomodoro_10_input_callback); + + return pomodoro_10; +} + +void pomodoro_10_free(PomodoroTimer* pomodoro_10) { + furi_assert(pomodoro_10); + view_free(pomodoro_10->view); + free(pomodoro_10); +} + +View* pomodoro_10_get_view(PomodoroTimer* pomodoro_10) { + furi_assert(pomodoro_10); + return pomodoro_10->view; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_10.h b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_10.h new file mode 100644 index 000000000..8f27e6bd6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_10.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "../pomodoro_timer.h" + +PomodoroTimer* pomodoro_10_alloc(); + +void pomodoro_10_free(PomodoroTimer* pomodoro_10); + +View* pomodoro_10_get_view(PomodoroTimer* pomodoro_10); diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_25.c b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_25.c new file mode 100644 index 000000000..01c5a7125 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_25.c @@ -0,0 +1,46 @@ +#include "../pomodoro_timer.h" +#include "pomodoro_25.h" +#include +#include +#include +#include + +static void pomodoro_25_draw_callback(Canvas* canvas, void* context) { + int max_seconds = 60 * 25; + int max_seconds_rest = 60 * 5; + pomodoro_draw_callback(canvas, context, max_seconds, max_seconds_rest); +} + +static bool pomodoro_25_input_callback(InputEvent* event, void* context) { + furi_assert(context); + PomodoroTimer* pomodoro_25 = context; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + return false; + } else { + pomodoro_timer_process(pomodoro_25, event); + return true; + } +} + +PomodoroTimer* pomodoro_25_alloc() { + PomodoroTimer* pomodoro_25 = malloc(sizeof(PomodoroTimer)); + pomodoro_25->view = view_alloc(); + view_set_context(pomodoro_25->view, pomodoro_25); + view_allocate_model(pomodoro_25->view, ViewModelTypeLocking, sizeof(PomodoroTimerModel)); + view_set_draw_callback(pomodoro_25->view, pomodoro_25_draw_callback); + view_set_input_callback(pomodoro_25->view, pomodoro_25_input_callback); + + return pomodoro_25; +} + +void pomodoro_25_free(PomodoroTimer* pomodoro_25) { + furi_assert(pomodoro_25); + view_free(pomodoro_25->view); + free(pomodoro_25); +} + +View* pomodoro_25_get_view(PomodoroTimer* pomodoro_25) { + furi_assert(pomodoro_25); + return pomodoro_25->view; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_25.h b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_25.h new file mode 100644 index 000000000..c3eb43976 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_25.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "../pomodoro_timer.h" + +PomodoroTimer* pomodoro_25_alloc(); + +void pomodoro_25_free(PomodoroTimer* pomodoro_25); + +View* pomodoro_25_get_view(PomodoroTimer* pomodoro_25); diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_50.c b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_50.c new file mode 100644 index 000000000..74f89122a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_50.c @@ -0,0 +1,46 @@ +#include "../pomodoro_timer.h" +#include "pomodoro_50.h" +#include +#include +#include +#include + +static void pomodoro_50_draw_callback(Canvas* canvas, void* context) { + int max_seconds = 60 * 50; + int max_seconds_rest = 60 * 10; + pomodoro_draw_callback(canvas, context, max_seconds, max_seconds_rest); +} + +static bool pomodoro_50_input_callback(InputEvent* event, void* context) { + furi_assert(context); + PomodoroTimer* pomodoro_50 = context; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + return false; + } else { + pomodoro_timer_process(pomodoro_50, event); + return true; + } +} + +PomodoroTimer* pomodoro_50_alloc() { + PomodoroTimer* pomodoro_50 = malloc(sizeof(PomodoroTimer)); + pomodoro_50->view = view_alloc(); + view_set_context(pomodoro_50->view, pomodoro_50); + view_allocate_model(pomodoro_50->view, ViewModelTypeLocking, sizeof(PomodoroTimerModel)); + view_set_draw_callback(pomodoro_50->view, pomodoro_50_draw_callback); + view_set_input_callback(pomodoro_50->view, pomodoro_50_input_callback); + + return pomodoro_50; +} + +void pomodoro_50_free(PomodoroTimer* pomodoro_50) { + furi_assert(pomodoro_50); + view_free(pomodoro_50->view); + free(pomodoro_50); +} + +View* pomodoro_50_get_view(PomodoroTimer* pomodoro_50) { + furi_assert(pomodoro_50); + return pomodoro_50->view; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_50.h b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_50.h new file mode 100644 index 000000000..e0246d2d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/pomodoro/views/pomodoro_50.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "../pomodoro_timer.h" + +PomodoroTimer* pomodoro_50_alloc(); + +void pomodoro_50_free(PomodoroTimer* pomodoro_50); + +View* pomodoro_50_get_view(PomodoroTimer* pomodoro_50); diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/protoview/LICENSE new file mode 100644 index 000000000..2d8a8a74d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2022-2023 Salvatore Sanfilippo + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/README.md b/Applications/Official/DEV_FW/source/xMasterX/protoview/README.md new file mode 100644 index 000000000..e7209d7d6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/README.md @@ -0,0 +1,103 @@ +ProtoView is a digital signal detection and visualization tool for the +[Flipper Zero](https://flipperzero.one/). The Flipper is able to identify +a great deal of RF protocols, however when the exact protocol is not +implemented (and there are many proprietary ones, such as the ones of +the car keys), the curious person is left wondering what the device is +sending at all. Using ProtoView she or he can visualize the high and low pulses +like in the example image below (showing a Volkswagen key in 2FSK): + +![ProtoView screenshot](/images/ProtoViewSignal.jpg) + +This is often enough to make an initial idea about the encoding used +and if the selected modulation is correct. + +The secondary goal of ProtoView is to provide a somewhat-documented application +for the Flipper (even if ProtoView is a pretty atypical application: doesn't make use of the standard widgets and other abstractions provded by the framework). +Many apps dealing with the *subghz subsystem* (the Flipper +abstraction to work with the [CC1101 chip](https://www.ti.com/product/CC1101)) +tend to be complicated and completely undocumented. This is unfortunately +true for the firmware of the device itself. It's a shame because especially +in the case of code that talks with hardware peripherals there are tons +of assumptions and hard-gained lessons that can [only be captured by comments and are in the code only implicitly](http://antirez.com/news/124). + +However, the Flipper firmware source code is well written even if it +lacks comments and documentation, so it is possible to make some ideas of +how things work just grepping inside. + +# Detection algorithm + +In order to show unknown signals, the application attempts to understand if +the samples obtained by the Flipper API (a series of pulses that are high +or low, and with different duration in microseconds) look like belonging to +a legitimate signal, and aren't just noise. + +We can't make assumptions about +the encoding and the data rate of the communication, so we use a simple +but relatively effective algorithm. As we check the signal, we try to detect +long parts of it that are composed of pulses roughly classifiable into +a maximum of three different classes of lengths, plus or minus 10%. Most +encodings are somewhat self-clocked, so they tend to have just two or +three classes of pulse lengths. + +However often pulses of the same theoretical +length have slightly different lenghts in the case of high and low level +(RF on or off), so we classify them separately for robustness. + +# Usage + +The application shows the longest coherent signal detected so far. + +* The OK button resets the current signal. +* The UP and DOWN buttons change the scale. Default is 100us per pixel. +* The LEFT and RIGHT buttons switch to settings. + +Under the detected sequence, you will see a small triangle marking a +specific sample. This mark means that the sequence looked coherent up +to that point, and starting from there it could be just noise. + +In the bottom-right corner the application displays an amount of time +in microseconds. This is the average length of the shortest pulse length +detected among the three classes. Usually the *data rate* of the protocol +is something like `1000000/this-number*2`, but it depends on the encoding +and could actually be `1000000/this-number*N` with `N > 2` (here 1000000 +is the number of microseconds in one second, and N is the number of clock +cycles needed to represent a bit). + +Things to investigate: + +* Many cheap remotes (gate openers, remotes, ...) are on the 433.92Mhz or nearby and use OOK modulation. +* Weather stations are often too in the 433.92Mhz OOK. +* For car keys, try 443.92 OOK650 and 868.35 Mhz in OOK or 2FSK. + +# Installing the app from source + +* Download the Flipper Zero dev kit and build it: +``` +mkdir -p ~/flipperZero/official/ +cd ~/flipperZero/official/ +git clone --recursive https://github.com/flipperdevices/flipperzero-firmware.git ./ +./fbt +``` +* Copy this application folder in `official/application_user`. +* Connect your Flipper via USB. +* Build and install with: `./fbt launch_app APPSRC=protoview`. + +# Installing the binary file (no build needed) + +Drop the `protoview.fap` file you can find in the `binaries` folder into the +following Flipper Zero location: + + /ext/apps/Tools + +The `ext` part means that we are in the SD card. So if you don't want +to use the Android (or other) application to upload the file, +you can just take out the SD card, insert it in your computer, +copy the fine into `apps/Tools`, and that's it. + +# License + +The code is released under the BSD license. + +# Disclaimer + +This application is only provided as an educational tool. The author is not liable in case the application is used to reverse engineer protocols protected by IP or for any other illegal purpose. diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/TODO b/Applications/Official/DEV_FW/source/xMasterX/protoview/TODO new file mode 100644 index 000000000..0003ccd38 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/TODO @@ -0,0 +1,20 @@ +Core improvements +================= + +- Detection of non Manchester and non RZ encoded signals. Not sure if there are any signals that are not self clocked widely used in RF. Note that the current approach already detects encodings using short high + long low and long high + short low to encode 0 and 1. In addition to the current classifier, it is possible to add one that checks for a sequence of pulses that are all multiples of some base length. This should detect, for instance, even NRZ encodings where 1 and 0 are just clocked as they are. + +- Views on-enter on-exit. + +Features +======== + +- Help screen (with press ok for next page). +- Detect the line code used and try to decode the message as hex dump. +- Pressing right/left you browse different modes: + * Current best signal pulse classes. + * Raw square wave display. Central button freezes and resumes (toggle). When frozen we display "paused" (inverted) on the low part of the screen. + +Screens sequence (user can navigate with <- and ->): + + (default) +[settings] <> [freq] <> [pulses view] <> [raw square view] <> [signal info] diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/app.c b/Applications/Official/DEV_FW/source/xMasterX/protoview/app.c new file mode 100644 index 000000000..6b88ce9d9 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/app.c @@ -0,0 +1,225 @@ +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + +#include "app.h" + +RawSamplesBuffer *RawSamples, *DetectedSamples; +extern const SubGhzProtocolRegistry protoview_protocol_registry; + +/* Draw some text with a border. If the outside color is black and the inside + * color is white, it just writes the border of the text, but the function can + * also be used to write a bold variation of the font setting both the + * colors to black, or alternatively to write a black text with a white + * border so that it is visible if there are black stuff on the background. */ +/* The callback actually just passes the control to the actual active + * view callback, after setting up basic stuff like cleaning the screen + * and setting color to black. */ +static void render_callback(Canvas *const canvas, void *ctx) { + ProtoViewApp *app = ctx; + + /* Clear screen. */ + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, 0, 127, 63); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + + /* Call who is in charge right now. */ + switch(app->current_view) { + case ViewRawPulses: render_view_raw_pulses(canvas,app); break; + case ViewInfo: render_view_info(canvas,app); break; + case ViewFrequencySettings: + case ViewModulationSettings: + render_view_settings(canvas,app); break; + case ViewLast: furi_crash(TAG " ViewLast selected"); break; + } +} + +/* Here all we do is putting the events into the queue that will be handled + * in the while() loop of the app entry point function. */ +static void input_callback(InputEvent* input_event, void* ctx) +{ + ProtoViewApp *app = ctx; + furi_message_queue_put(app->event_queue,input_event,FuriWaitForever); +} + +/* Allocate the application state and initialize a number of stuff. + * This is called in the entry point to create the application state. */ +ProtoViewApp* protoview_app_alloc() { + ProtoViewApp *app = malloc(sizeof(ProtoViewApp)); + + // Init shared data structures + RawSamples = raw_samples_alloc(); + DetectedSamples = raw_samples_alloc(); + + //init setting + app->setting = subghz_setting_alloc(); + subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user")); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + app->view_port = view_port_alloc(); + view_port_draw_callback_set(app->view_port, render_callback, app); + view_port_input_callback_set(app->view_port, input_callback, app); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + app->current_view = ViewRawPulses; + + // Signal found and visualization defaults + app->signal_bestlen = 0; + app->signal_decoded = false; + app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE; + app->signal_offset = 0; + + //init Worker & Protocol + app->txrx = malloc(sizeof(ProtoViewTxRx)); + + /* Setup rx worker and environment. */ + app->txrx->worker = subghz_worker_alloc(); + app->txrx->environment = subghz_environment_alloc(); + subghz_environment_set_protocol_registry( + app->txrx->environment, (void*)&protoview_protocol_registry); + app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment); + + subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable); + subghz_worker_set_overrun_callback( + app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); + subghz_worker_set_pair_callback( + app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); + subghz_worker_set_context(app->txrx->worker, app->txrx->receiver); + + app->frequency = subghz_setting_get_default_frequency(app->setting); + app->modulation = 0; /* Defaults to ProtoViewModulations[0]. */ + + furi_hal_power_suppress_charge_enter(); + app->running = 1; + + return app; +} + +/* Free what the application allocated. It is not clear to me if the + * Flipper OS, once the application exits, will be able to reclaim space + * even if we forget to free something here. */ +void protoview_app_free(ProtoViewApp *app) { + furi_assert(app); + + // Put CC1101 on sleep. + radio_sleep(app); + + // View related. + view_port_enabled_set(app->view_port, false); + gui_remove_view_port(app->gui, app->view_port); + view_port_free(app->view_port); + furi_record_close(RECORD_GUI); + furi_message_queue_free(app->event_queue); + app->gui = NULL; + + // Frequency setting. + subghz_setting_free(app->setting); + + // Worker stuff. + subghz_receiver_free(app->txrx->receiver); + subghz_environment_free(app->txrx->environment); + subghz_worker_free(app->txrx->worker); + free(app->txrx); + + // Raw samples buffers. + raw_samples_free(RawSamples); + raw_samples_free(DetectedSamples); + furi_hal_power_suppress_charge_exit(); + + free(app); +} + +/* Called periodically. Do signal processing here. Data we process here + * will be later displayed by the render callback. The side effect of this + * function is to scan for signals and set DetectedSamples. */ +static void timer_callback(void *ctx) { + ProtoViewApp *app = ctx; + scan_for_signal(app); +} + +int32_t protoview_app_entry(void* p) { + UNUSED(p); + ProtoViewApp *app = protoview_app_alloc(); + + /* Create a timer. We do data analysis in the callback. */ + FuriTimer *timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); + + /* Start listening to signals immediately. */ + radio_begin(app); + radio_rx(app); + + /* This is the main event loop: here we get the events that are pushed + * in the queue by input_callback(), and process them one after the + * other. The timeout is 100 milliseconds, so if not input is received + * before such time, we exit the queue_get() function and call + * view_port_update() in order to refresh our screen content. */ + InputEvent input; + while(app->running) { + FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100); + if (qstat == FuriStatusOk) { + if (DEBUG_MSG) FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", + input.type, input.key); + + /* Handle navigation here. Then handle view-specific inputs + * in the view specific handling function. */ + if (input.type == InputTypeShort && + input.key == InputKeyBack) + { + /* Exit the app. */ + app->running = 0; + } else if (input.type == InputTypeShort && + input.key == InputKeyRight) + { + /* Go to the next view. */ + app->current_view++; + if (app->current_view == ViewLast) app->current_view = 0; + } else if (input.type == InputTypeShort && + input.key == InputKeyLeft) + { + /* Go to the previous view. */ + if (app->current_view == 0) + app->current_view = ViewLast-1; + else + app->current_view--; + } else { + /* This is where we pass the control to the currently + * active view input processing. */ + switch(app->current_view) { + case ViewRawPulses: + process_input_raw_pulses(app,input); + break; + case ViewInfo: + process_input_info(app,input); + break; + case ViewFrequencySettings: + case ViewModulationSettings: + process_input_settings(app,input); + break; + case ViewLast: furi_crash(TAG " ViewLast selected"); break; + } + } + } else { + /* Useful to understand if the app is still alive when it + * does not respond because of bugs. */ + if (DEBUG_MSG) { + static int c = 0; c++; + if (!(c % 20)) FURI_LOG_E(TAG, "Loop timeout"); + } + } + view_port_update(app->view_port); + } + + /* App no longer running. Shut down and free. */ + if (app->txrx->txrx_state == TxRxStateRx) { + FURI_LOG_E(TAG, "Putting CC1101 to sleep before exiting."); + radio_rx_end(app); + radio_sleep(app); + } + + furi_timer_free(timer); + protoview_app_free(app); + return 0; +} + diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/app.h b/Applications/Official/DEV_FW/source/xMasterX/protoview/app.h new file mode 100644 index 000000000..38e90416e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/app.h @@ -0,0 +1,154 @@ +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "app_buffer.h" + +#define TAG "ProtoView" +#define PROTOVIEW_RAW_VIEW_DEFAULT_SCALE 100 +#define BITMAP_SEEK_NOT_FOUND UINT32_MAX + +#define DEBUG_MSG 1 + +typedef struct ProtoViewApp ProtoViewApp; + +/* Subghz system state */ +typedef enum { + TxRxStateIDLE, + TxRxStateRx, + TxRxStateSleep, +} TxRxState; + +/* Currently active view. */ +typedef enum { + ViewRawPulses, + ViewInfo, + ViewFrequencySettings, + ViewModulationSettings, + ViewLast, /* Just a sentinel to wrap around. */ +} ProtoViewCurrentView; + +typedef struct { + const char *name; + FuriHalSubGhzPreset preset; + uint8_t *custom; +} ProtoViewModulation; + +extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */ + +/* This is the context of our subghz worker and associated thread. + * It receives data and we get our protocol "feed" callback called + * with the level (1 or 0) and duration. */ +struct ProtoViewTxRx { + SubGhzWorker* worker; /* Our background worker. */ + SubGhzEnvironment* environment; + SubGhzReceiver* receiver; + TxRxState txrx_state; /* Receiving, idle or sleeping? */ +}; + +typedef struct ProtoViewTxRx ProtoViewTxRx; + +/* This stucture is filled by the decoder for specific protocols with the + * informations about the message. ProtoView will display such information + * in the message info view. */ +#define PROTOVIEW_MSG_STR_LEN 32 +typedef struct ProtoViewMsgInfo { + char name[PROTOVIEW_MSG_STR_LEN]; /* Protocol name and version. */ + char raw[PROTOVIEW_MSG_STR_LEN]; /* Protocol specific raw representation.*/ + /* The following is what the decoder wants to show to user. Each decoder + * can use the number of fileds it needs. */ + char info1[PROTOVIEW_MSG_STR_LEN]; /* Protocol specific info line 1. */ + char info2[PROTOVIEW_MSG_STR_LEN]; /* Protocol specific info line 2. */ + char info3[PROTOVIEW_MSG_STR_LEN]; /* Protocol specific info line 3. */ + uint64_t len; /* Bits consumed from the stream. */ +} ProtoViewMsgInfo; + +struct ProtoViewApp { + /* GUI */ + Gui *gui; + ViewPort *view_port; /* We just use a raw viewport and we render + everything into the low level canvas. */ + ProtoViewCurrentView current_view; /* Active view ID. */ + FuriMessageQueue *event_queue; /* Keypress events go here. */ + + /* Radio related. */ + ProtoViewTxRx *txrx; /* Radio state. */ + SubGhzSetting *setting; /* A list of valid frequencies. */ + + /* Generic app state. */ + int running; /* Once false exists the app. */ + uint32_t signal_bestlen; /* Longest coherent signal observed so far. */ + bool signal_decoded; /* Was the current signal decoded? */ + ProtoViewMsgInfo signal_info; /* Decoded message, if signal_decoded true. */ + + /* Raw view apps state. */ + uint32_t us_scale; /* microseconds per pixel. */ + uint32_t signal_offset; /* Long press left/right panning in raw view. */ + + /* Configuration view app state. */ + uint32_t frequency; /* Current frequency. */ + uint8_t modulation; /* Current modulation ID, array index in the + ProtoViewModulations table. */ +}; + +typedef struct ProtoViewDecoder { + const char *name; /* Protocol name. */ + /* The decode function takes a buffer that is actually a bitmap, with + * high and low levels represented as 0 and 1. The number of high/low + * pulses represented by the bitmap is passed as the 'numbits' argument, + * while 'numbytes' represents the total size of the bitmap pointed by + * 'bits'. So 'numbytes' is mainly useful to pass as argument to other + * functions that perform bit extraction with bound checking, such as + * bitmap_get() and so forth. */ + bool (*decode)(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info); +} ProtoViewDecoder; + +extern RawSamplesBuffer *RawSamples, *DetectedSamples; + +/* app_radio.c */ +void radio_begin(ProtoViewApp* app); +uint32_t radio_rx(ProtoViewApp* app); +void radio_idle(ProtoViewApp* app); +void radio_rx_end(ProtoViewApp* app); +void radio_sleep(ProtoViewApp* app); + +/* signal.c */ +uint32_t duration_delta(uint32_t a, uint32_t b); +void reset_current_signal(ProtoViewApp *app); +void scan_for_signal(ProtoViewApp *app); +bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos); +void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val); +void bitmap_set_pattern(uint8_t *b, uint32_t blen, const char *pat); +void bitmap_invert_bytes_bits(uint8_t *p, uint32_t len); +bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits); +uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t maxbits, const char *bits); +uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t offset, const char *zero_pattern, const char *one_pattern); + +/* view_*.c */ +void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app); +void process_input_raw_pulses(ProtoViewApp *app, InputEvent input); +void render_view_settings(Canvas *const canvas, ProtoViewApp *app); +void process_input_settings(ProtoViewApp *app, InputEvent input); +void render_view_info(Canvas *const canvas, ProtoViewApp *app); +void process_input_info(ProtoViewApp *app, InputEvent input); + +/* ui.c */ +void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color); diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/app_buffer.c b/Applications/Official/DEV_FW/source/xMasterX/protoview/app_buffer.c new file mode 100644 index 000000000..df2e7074e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/app_buffer.c @@ -0,0 +1,73 @@ +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + +#include +#include +#include +#include +#include "app_buffer.h" + +/* Allocate and initialize a samples buffer. */ +RawSamplesBuffer *raw_samples_alloc(void) { + RawSamplesBuffer *buf = malloc(sizeof(*buf)); + buf->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + raw_samples_reset(buf); + return buf; +} + +/* Free a sample buffer. Should be called when the mutex is released. */ +void raw_samples_free(RawSamplesBuffer *s) { + furi_mutex_free(s->mutex); + free(s); +} + +/* This just set all the samples to zero and also resets the internal + * index. There is no need to call it after raw_samples_alloc(), but only + * when one wants to reset the whole buffer of samples. */ +void raw_samples_reset(RawSamplesBuffer *s) { + furi_mutex_acquire(s->mutex,FuriWaitForever); + s->total = RAW_SAMPLES_NUM; + s->idx = 0; + s->short_pulse_dur = 0; + memset(s->level,0,sizeof(s->level)); + memset(s->dur,0,sizeof(s->dur)); + furi_mutex_release(s->mutex); +} + +/* Set the raw sample internal index so that what is currently at + * offset 'offset', will appear to be at 0 index. */ +void raw_samples_center(RawSamplesBuffer *s, uint32_t offset) { + s->idx = (s->idx+offset) % RAW_SAMPLES_NUM; +} + +/* Add the specified sample in the circular buffer. */ +void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur) { + furi_mutex_acquire(s->mutex,FuriWaitForever); + s->level[s->idx] = level; + s->dur[s->idx] = dur; + s->idx = (s->idx+1) % RAW_SAMPLES_NUM; + furi_mutex_release(s->mutex); +} + +/* Get the sample from the buffer. It is possible to use out of range indexes + * as 'idx' because the modulo operation will rewind back from the start. */ +void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *dur) +{ + furi_mutex_acquire(s->mutex,FuriWaitForever); + idx = (s->idx + idx) % RAW_SAMPLES_NUM; + *level = s->level[idx]; + *dur = s->dur[idx]; + furi_mutex_release(s->mutex); +} + +/* Copy one buffer to the other, including current index. */ +void raw_samples_copy(RawSamplesBuffer *dst, RawSamplesBuffer *src) { + furi_mutex_acquire(src->mutex,FuriWaitForever); + furi_mutex_acquire(dst->mutex,FuriWaitForever); + dst->idx = src->idx; + dst->short_pulse_dur = src->short_pulse_dur; + memcpy(dst->level,src->level,sizeof(dst->level)); + memcpy(dst->dur,src->dur,sizeof(dst->dur)); + furi_mutex_release(src->mutex); + furi_mutex_release(dst->mutex); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/app_buffer.h b/Applications/Official/DEV_FW/source/xMasterX/protoview/app_buffer.h new file mode 100644 index 000000000..5d997d02e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/app_buffer.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + +/* Our circular buffer of raw samples, used in order to display + * the signal. */ + +#define RAW_SAMPLES_NUM 2048 /* Use a power of two: we take the modulo + of the index quite often to normalize inside + the range, and division is slow. */ + +typedef struct RawSamplesBuffer { + FuriMutex *mutex; + uint8_t level[RAW_SAMPLES_NUM]; + uint32_t dur[RAW_SAMPLES_NUM]; + uint32_t idx; /* Current idx (next to write). */ + uint32_t total; /* Total samples: same as RAW_SAMPLES_NUM, we provide + this field for a cleaner interface with the user, but + we always use RAW_SAMPLES_NUM when taking the modulo so + the compiler can optimize % as bit masking. */ + /* Signal features. */ + uint32_t short_pulse_dur; /* Duration of the shortest pulse. */ +} RawSamplesBuffer; + +RawSamplesBuffer *raw_samples_alloc(void); +void raw_samples_reset(RawSamplesBuffer *s); +void raw_samples_center(RawSamplesBuffer *s, uint32_t offset); +void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur); +void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *dur); +void raw_samples_copy(RawSamplesBuffer *dst, RawSamplesBuffer *src); +void raw_samples_free(RawSamplesBuffer *s); diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/app_subghz.c b/Applications/Official/DEV_FW/source/xMasterX/protoview/app_subghz.c new file mode 100644 index 000000000..987dd1aac --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/app_subghz.c @@ -0,0 +1,85 @@ +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + +#include "app.h" +#include "custom_presets.h" + +#include + +ProtoViewModulation ProtoViewModulations[] = { + {"OOK 650Khz", FuriHalSubGhzPresetOok650Async, NULL}, + {"OOK 270Khz", FuriHalSubGhzPresetOok270Async, NULL}, + {"2FSK 2.38Khz", FuriHalSubGhzPreset2FSKDev238Async, NULL}, + {"2FSK 47.6Khz", FuriHalSubGhzPreset2FSKDev476Async, NULL}, + {"MSK", FuriHalSubGhzPresetMSK99_97KbAsync, NULL}, + {"GFSK", FuriHalSubGhzPresetGFSK9_99KbAsync, NULL}, + {"FSK for TPMS", 0, (uint8_t*)protoview_subghz_tpms_async_regs}, + {NULL, 0, NULL} /* End of list sentinel. */ +}; + +/* Called after the application initialization in order to setup the + * subghz system and put it into idle state. If the user wants to start + * receiving we will call radio_rx() to start a receiving worker and + * associated thread. */ +void radio_begin(ProtoViewApp* app) { + furi_assert(app); + furi_hal_subghz_reset(); + furi_hal_subghz_idle(); + + /* The CC1101 preset can be either one of the standard presets, if + * the modulation "custom" field is NULL, or a custom preset we + * defined in custom_presets.h. */ + if (ProtoViewModulations[app->modulation].custom == NULL) + furi_hal_subghz_load_preset(ProtoViewModulations[app->modulation].preset); + else + furi_hal_subghz_load_custom_preset(ProtoViewModulations[app->modulation].custom); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + app->txrx->txrx_state = TxRxStateIDLE; +} + +/* Setup subghz to start receiving using a background worker. */ +uint32_t radio_rx(ProtoViewApp* app) { + furi_assert(app); + if(!furi_hal_subghz_is_frequency_valid(app->frequency)) { + furi_crash(TAG" Incorrect RX frequency."); + } + + if (app->txrx->txrx_state == TxRxStateRx) return app->frequency; + + furi_hal_subghz_idle(); /* Put it into idle state in case it is sleeping. */ + uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency); + FURI_LOG_E(TAG, "Switched to frequency: %lu", value); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_subghz_flush_rx(); + furi_hal_subghz_rx(); + + furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, app->txrx->worker); + subghz_worker_start(app->txrx->worker); + app->txrx->txrx_state = TxRxStateRx; + return value; +} + +/* Stop subghz worker (if active), put radio on idle state. */ +void radio_rx_end(ProtoViewApp* app) { + furi_assert(app); + if (app->txrx->txrx_state == TxRxStateRx) { + if(subghz_worker_is_running(app->txrx->worker)) { + subghz_worker_stop(app->txrx->worker); + furi_hal_subghz_stop_async_rx(); + } + } + furi_hal_subghz_idle(); + app->txrx->txrx_state = TxRxStateIDLE; +} + +/* Put radio on sleep. */ +void radio_sleep(ProtoViewApp* app) { + furi_assert(app); + if (app->txrx->txrx_state == TxRxStateRx) { + /* We can't go from having an active RX worker to sleeping. + * Stop the RX subsystems first. */ + radio_rx_end(app); + } + furi_hal_subghz_sleep(); + app->txrx->txrx_state = TxRxStateSleep; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/appicon.png b/Applications/Official/DEV_FW/source/xMasterX/protoview/appicon.png new file mode 100644 index 000000000..7ce5c4eff Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/protoview/appicon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/application.fam b/Applications/Official/DEV_FW/source/xMasterX/protoview/application.fam new file mode 100644 index 000000000..c620d5ec8 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/application.fam @@ -0,0 +1,12 @@ +App( + appid="protoview", + name="Protocols visualizer", + apptype=FlipperAppType.EXTERNAL, + entry_point="protoview_app_entry", + cdefines=["APP_PROTOVIEW"], + requires=["gui"], + stack_size=8 * 1024, + order=50, + fap_icon="appicon.png", + fap_category="Misc_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/custom_presets.h b/Applications/Official/DEV_FW/source/xMasterX/protoview/custom_presets.h new file mode 100644 index 000000000..616f87bf7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/custom_presets.h @@ -0,0 +1,46 @@ +#include + +static uint8_t protoview_subghz_tpms_async_regs[][2] = { + /* GPIO GD0 */ + {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input + + /* Frequency Synthesizer Control */ + {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz + + /* Packet engine */ + {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening + {CC1101_PKTCTRL1, 0x04}, + + // // Modem Configuration + {CC1101_MDMCFG0, 0x00}, + {CC1101_MDMCFG1, 0x02}, + {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. + {CC1101_MDMCFG3, 0x93}, // Data rate is 20kBaud + {CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz + {CC1101_DEVIATN, 0x41}, // Deviation 28.56 kHz + + /* Main Radio Control State Machine */ + {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + {CC1101_FOCCFG, + 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + {CC1101_AGCCTRL0, + 0x91}, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + {CC1101_AGCCTRL1, + 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB + + /* Wake on radio and timeouts control */ + {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer + {CC1101_FREND1, 0x56}, + + /* End */ + {0, 0}, +}; + diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/images/ProtoViewSignal.jpg b/Applications/Official/DEV_FW/source/xMasterX/protoview/images/ProtoViewSignal.jpg new file mode 100644 index 000000000..2c1938e7b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/protoview/images/ProtoViewSignal.jpg differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/proto.c b/Applications/Official/DEV_FW/source/xMasterX/protoview/proto.c new file mode 100644 index 000000000..c3387fc4c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/proto.c @@ -0,0 +1,120 @@ +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + +#include +#include +#include +#include +#include +#include "app_buffer.h" + +#define TAG "PROTOVIEW-protocol" + +const SubGhzProtocol subghz_protocol_protoview; + +/* The feed() method puts data in the RawSamples global (protected by + * a mutex). */ +extern RawSamplesBuffer *RawSamples; + +/* This is totally dummy: we just define the decoder base for the async + * system to work but we don't really use it if not to collect raw + * data via the feed() method. */ +typedef struct SubGhzProtocolDecoderprotoview { + SubGhzProtocolDecoderBase base; +} SubGhzProtocolDecoderprotoview; + +void* subghz_protocol_decoder_protoview_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + + SubGhzProtocolDecoderprotoview* instance = + malloc(sizeof(SubGhzProtocolDecoderprotoview)); + instance->base.protocol = &subghz_protocol_protoview; + return instance; +} + +void subghz_protocol_decoder_protoview_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderprotoview* instance = context; + free(instance); +} + +void subghz_protocol_decoder_protoview_reset(void* context) { + furi_assert(context); +} + +/* That's the only thig we really use of the protocol decoder + * implementation. We avoid the subghz provided abstractions and put + * the data in our simple abstraction: the RawSamples circular buffer. */ +void subghz_protocol_decoder_protoview_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + UNUSED(context); + + /* Add data to the circular buffer. */ + raw_samples_add(RawSamples, level, duration); + // FURI_LOG_E(TAG, "FEED: %d %d", (int)level, (int)duration); + return; +} + +/* The only scope of this method is to avoid duplicated messages in the + * Subghz history, which we don't use. */ +uint8_t subghz_protocol_decoder_protoview_get_hash_data(void* context) { + furi_assert(context); + return 123; +} + +/* Not used. */ +bool subghz_protocol_decoder_protoview_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) +{ + UNUSED(context); + UNUSED(flipper_format); + UNUSED(preset); + return false; +} + +/* Not used. */ +bool subghz_protocol_decoder_protoview_deserialize(void* context, FlipperFormat* flipper_format) +{ + UNUSED(context); + UNUSED(flipper_format); + return false; +} + +void subhz_protocol_decoder_protoview_get_string(void* context, FuriString* output) +{ + furi_assert(context); + furi_string_cat_printf(output, "Protoview"); +} + +const SubGhzProtocolDecoder subghz_protocol_protoview_decoder = { + .alloc = subghz_protocol_decoder_protoview_alloc, + .free = subghz_protocol_decoder_protoview_free, + .reset = subghz_protocol_decoder_protoview_reset, + .feed = subghz_protocol_decoder_protoview_feed, + .get_hash_data = subghz_protocol_decoder_protoview_get_hash_data, + .serialize = subghz_protocol_decoder_protoview_serialize, + .deserialize = subghz_protocol_decoder_protoview_deserialize, + .get_string = subhz_protocol_decoder_protoview_get_string, +}; + +/* Well, we don't really target a specific protocol. So let's put flags + * that make sense. */ +const SubGhzProtocol subghz_protocol_protoview = { + .name = "Protoview", + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable, + .decoder = &subghz_protocol_protoview_decoder, +}; + +/* Our table has just the single dummy protocol we defined for the + * sake of data collection. */ +const SubGhzProtocol* protoview_protocol_registry_items[] = { + &subghz_protocol_protoview, +}; + +const SubGhzProtocolRegistry protoview_protocol_registry = { + .items = protoview_protocol_registry_items, + .size = COUNT_OF(protoview_protocol_registry_items) +}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/protocols/b4b1.c b/Applications/Official/DEV_FW/source/xMasterX/protoview/protocols/b4b1.c new file mode 100644 index 000000000..6977c0eb3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/protocols/b4b1.c @@ -0,0 +1,44 @@ +/* PT/SC remotes. Usually 443.92 Mhz OOK. + * + * This line code is used in many remotes such as Princeton chips + * named PT, Silian Microelectronics SC5262 and others. + * Basically every 4 pulsee represent a bit, where 1000 means 0, and + * 1110 means 1. Usually we can read 24 bits of data. + * In this specific implementation we check for a prelude that is + * 1 bit high, 31 bits low, but the check is relaxed. */ + +#include "../app.h" + +static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { + if (numbits < 30) return false; + const char *sync_patterns[3] = { + "10000000000000000000000000000001", /* 30 zero bits. */ + "100000000000000000000000000000001", /* 31 zero bits. */ + "1000000000000000000000000000000001", /* 32 zero bits. */ + }; + + uint32_t off; + int j; + for (j = 0; j < 3; j++) { + off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_patterns[j]); + if (off != BITMAP_SEEK_NOT_FOUND) break; + } + if (off == BITMAP_SEEK_NOT_FOUND) return false; + if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu",off); + off += strlen(sync_patterns[j])-1; + + uint8_t d[3]; /* 24 bits of data. */ + uint32_t decoded = + convert_from_line_code(d,sizeof(d),bits,numbytes,off,"1000","1110"); + + if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu",decoded); + if (decoded != 24) return false; + snprintf(info->name,PROTOVIEW_MSG_STR_LEN,"PT/SC remote"); + snprintf(info->raw,PROTOVIEW_MSG_STR_LEN,"%02X%02X%02X",d[0],d[1],d[2]); + info->len = off+(4*24); + return true; +} + +ProtoViewDecoder B4B1Decoder = { + "B4B1", decode +}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/protocols/oregon2.c b/Applications/Official/DEV_FW/source/xMasterX/protoview/protocols/oregon2.c new file mode 100644 index 000000000..3aa57c72d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/protocols/oregon2.c @@ -0,0 +1,65 @@ +/* Oregon remote termometers. Usually 443.92 Mhz OOK. + * + * The protocol is described here: + * https://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf + * This implementation is not very complete. */ + +#include "../app.h" + +static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { + if (numbits < 32) return false; + const char *sync_pattern = "01100110" "01100110" "10010110" "10010110"; + uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); + if (off == BITMAP_SEEK_NOT_FOUND) return false; + FURI_LOG_E(TAG, "Oregon2 preamble+sync found"); + + off += 32; /* Skip preamble. */ + + uint8_t buffer[8], raw[8] = {0}; + uint32_t decoded = + convert_from_line_code(buffer,sizeof(buffer),bits,numbytes,off,"1001","0110"); + FURI_LOG_E(TAG, "Oregon2 decoded bits: %lu", decoded); + + if (decoded < 11*4) return false; /* Minimum len to extract some data. */ + + char temp[3] = {0}, deviceid[2] = {0}, hum[2] = {0}; + for (int j = 0; j < 64; j += 4) { + uint8_t nib[1]; + nib[0] = (bitmap_get(buffer,8,j+0) | + bitmap_get(buffer,8,j+1) << 1 | + bitmap_get(buffer,8,j+2) << 2 | + bitmap_get(buffer,8,j+3) << 3); + if (DEBUG_MSG) FURI_LOG_E(TAG, "Not inverted nibble[%d]: %x", j/4, (unsigned int)nib[0]); + raw[j/8] |= nib[0] << (4-(j%4)); + switch(j/4) { + case 1: deviceid[0] |= nib[0]; break; + case 0: deviceid[0] |= nib[0] << 4; break; + case 3: deviceid[1] |= nib[0]; break; + case 2: deviceid[1] |= nib[0] << 4; break; + case 10: temp[0] = nib[0]; break; + /* Fixme: take the temperature sign from nibble 11. */ + case 9: temp[1] = nib[0]; break; + case 8: temp[2] = nib[0]; break; + case 13: hum[0] = nib[0]; break; + case 12: hum[1] = nib[0]; break; + } + } + + snprintf(info->name,sizeof(info->name),"%s","Oregon v2.1"); + /* The following line crashes the Flipper because of broken + * snprintf() implementation. */ + snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X", + raw[0],raw[1],raw[2],raw[3],raw[4],raw[5], + raw[6],raw[7]); + snprintf(info->info1,sizeof(info->info1),"Sensor ID %02X%02X", + deviceid[0], deviceid[1]); + snprintf(info->info2,sizeof(info->info2),"Temperature %d%d.%d", + temp[0],temp[1],temp[2]); + snprintf(info->info3,sizeof(info->info3),"Humidity %d%d", + hum[0],hum[1]); + return true; +} + +ProtoViewDecoder Oregon2Decoder = { + "Oregon2", decode +}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/protocols/oregon2.txt b/Applications/Official/DEV_FW/source/xMasterX/protoview/protocols/oregon2.txt new file mode 100644 index 000000000..362631431 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/protocols/oregon2.txt @@ -0,0 +1,6 @@ +11001100110011001100110011001100110011001100110011001100110 (Preamble) +10 01 01 10 10 01 01 10 (Sync) +01 10 10 01 10 01 10 01 01 10 10 01 01 10 01 10 10 01 01 10 10 01 10 01 10 01 10 01 10 01 10 01 01 10 10 01 10 01 10 01 01 10 01 10 01 10 01 10 01 10 01 10 10 01 01 10 01 10 10 01 10 01 10 01 10 01 10 01 01 10 10 01 10 01 01 10 01 10 10 01 01 10 10 01 10 01 10 01 10 01 10 01 10 01 11 0 + +We need to seek the following bytes: 01100110 01100110 10010110 10010110 + 0x66 0x66 96 96 diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/protocols/renault_tpms.c b/Applications/Official/DEV_FW/source/xMasterX/protoview/protocols/renault_tpms.c new file mode 100644 index 000000000..3022a5d4e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/protocols/renault_tpms.c @@ -0,0 +1,63 @@ +/* Renault tires TPMS. Usually 443.92 Mhz FSK. + * + * Preamble + marshal-encoded bits. 9 Bytes in total if we don't + * count the preamble. */ + +#include "../app.h" + +#define USE_TEST_VECTOR 0 +static const char *test_vector = + "10101010" "10101010" "10101010" "10101001" // Preamble + sync. + + /* The following is marshal encoded, so each two characters are + * actaully one bit. 01 = 1, 10 = 0. */ + "010110010110" // Flags. + "10011001101010011001" // Pressure, multiply by 0.75 to obtain kpa. + // 244 kpa here. + "1010010110011010" // Temperature, subtract 30 to obtain celsius. 22C here. + "1001010101101001" + "0101100110010101" + "1001010101100110" // Tire ID. 0x7AD779 here. + "0101010101010101" + "0101010101010101" // Two FF bytes (usually). Unknown. + "0110010101010101"; // CRC8 with (poly 7, initialization 0). + +static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { + + if (USE_TEST_VECTOR) { /* Test vector to check that decoding works. */ + bitmap_set_pattern(bits,numbytes,test_vector); + numbits = strlen(test_vector); + } + + if (numbits < 13*8) return false; + + const char *sync_pattern = "10101010" "10101010" "10101010" "10101001"; + uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); + if (off == BITMAP_SEEK_NOT_FOUND) return false; + FURI_LOG_E(TAG, "Renault TPMS preamble+sync found"); + + off += 32; /* Skip preamble. */ + + uint8_t raw[9]; + uint32_t decoded = + convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, + "10","01"); /* Manchester. */ + FURI_LOG_E(TAG, "Renault TPMS decoded bits: %lu", decoded); + + if (decoded < 8*9) return false; /* Require the full 9 bytes. */ + + float kpa = 0.75 *((uint32_t)((raw[0]&3)<<8) | raw[1]); + int temp = raw[2]-30; + + snprintf(info->name,sizeof(info->name),"%s","Renault TPMS"); + snprintf(info->raw,sizeof(info->raw),"%02X%02X%02X%02X%02X%02X%02X%02X%02X", + raw[0],raw[1],raw[2],raw[3],raw[4],raw[5], + raw[6],raw[7],raw[8]); + snprintf(info->info1,sizeof(info->info1),"Pressure %.2f kpa", (double)kpa); + snprintf(info->info2,sizeof(info->info2),"Temperature %d C", temp); + return true; +} + +ProtoViewDecoder RenaultTPMSDecoder = { + "Renault TPMS", decode +}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/signal.c b/Applications/Official/DEV_FW/source/xMasterX/protoview/signal.c new file mode 100644 index 000000000..2ff632811 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/signal.c @@ -0,0 +1,420 @@ +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + +#include "app.h" + +bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info); +void initialize_msg_info(ProtoViewMsgInfo *i); + +/* ============================================================================= + * Raw signal detection + * ===========================================================================*/ + +/* Return the time difference between a and b, always >= 0 since + * the absolute value is returned. */ +uint32_t duration_delta(uint32_t a, uint32_t b) { + return a > b ? a - b : b - a; +} + +/* Reset the current signal, so that a new one can be detected. */ +void reset_current_signal(ProtoViewApp *app) { + app->signal_bestlen = 0; + app->signal_offset = 0; + app->signal_decoded = false; + raw_samples_reset(DetectedSamples); + raw_samples_reset(RawSamples); +} + +/* This function starts scanning samples at offset idx looking for the + * longest run of pulses, either high or low, that are not much different + * from each other, for a maximum of three duration classes. + * So for instance 50 successive pulses that are roughly long 340us or 670us + * will be sensed as a coherent signal (example: 312, 361, 700, 334, 667, ...) + * + * The classes are counted separtely for high and low signals (RF on / off) + * because many devices tend to have different pulse lenghts depending on + * the level of the pulse. + * + * For instance Oregon2 sensors, in the case of protocol 2.1 will send + * pulses of ~400us (RF on) VS ~580us (RF off). */ +#define SEARCH_CLASSES 3 +uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) { + struct { + uint32_t dur[2]; /* dur[0] = low, dur[1] = high */ + uint32_t count[2]; /* Associated observed frequency. */ + } classes[SEARCH_CLASSES]; + + memset(classes,0,sizeof(classes)); + uint32_t minlen = 30, maxlen = 4000; /* Depends on data rate, here we + allow for high and low. */ + uint32_t len = 0; /* Observed len of coherent samples. */ + s->short_pulse_dur = 0; + for (uint32_t j = idx; j < idx+500; j++) { + bool level; + uint32_t dur; + raw_samples_get(s, j, &level, &dur); + if (dur < minlen || dur > maxlen) break; /* return. */ + + /* Let's see if it matches a class we already have or if we + * can populate a new (yet empty) class. */ + uint32_t k; + for (k = 0; k < SEARCH_CLASSES; k++) { + if (classes[k].count[level] == 0) { + classes[k].dur[level] = dur; + classes[k].count[level] = 1; + break; /* Sample accepted. */ + } else { + uint32_t classavg = classes[k].dur[level]; + uint32_t count = classes[k].count[level]; + uint32_t delta = duration_delta(dur,classavg); + /* Is the difference in duration between this signal and + * the class we are inspecting less than a given percentage? + * If so, accept this signal. */ + if (delta < classavg/8) { /* 100%/8 = 12%. */ + /* It is useful to compute the average of the class + * we are observing. We know how many samples we got so + * far, so we can recompute the average easily. + * By always having a better estimate of the pulse len + * we can avoid missing next samples in case the first + * observed samples are too off. */ + classavg = ((classavg * count) + dur) / (count+1); + classes[k].dur[level] = classavg; + classes[k].count[level]++; + break; /* Sample accepted. */ + } + } + } + + if (k == SEARCH_CLASSES) break; /* No match, return. */ + + /* If we are here, we accepted this sample. Try with the next + * one. */ + len++; + } + + /* Update the buffer setting the shortest pulse we found + * among the three classes. This will be used when scaling + * for visualization. */ + uint32_t short_dur[2] = {0,0}; + for (int j = 0; j < SEARCH_CLASSES; j++) { + for (int level = 0; level < 2; level++) { + if (classes[j].dur[level] == 0) continue; + if (classes[j].count[level] < 3) continue; + if (short_dur[level] == 0 || + short_dur[level] > classes[j].dur[level]) + { + short_dur[level] = classes[j].dur[level]; + } + } + } + + /* Use the average between high and low short pulses duration. + * Often they are a bit different, and using the average is more robust + * when we do decoding sampling at short_pulse_dur intervals. */ + if (short_dur[0] == 0) short_dur[0] = short_dur[1]; + if (short_dur[1] == 0) short_dur[1] = short_dur[0]; + s->short_pulse_dur = (short_dur[0]+short_dur[1])/2; + + return len; +} + +/* Search the buffer with the stored signal (last N samples received) + * in order to find a coherent signal. If a signal that does not appear to + * be just noise is found, it is set in DetectedSamples global signal + * buffer, that is what is rendered on the screen. */ +void scan_for_signal(ProtoViewApp *app) { + /* We need to work on a copy: the RawSamples buffer is populated + * by the background thread receiving data. */ + RawSamplesBuffer *copy = raw_samples_alloc(); + raw_samples_copy(copy,RawSamples); + + /* Try to seek on data that looks to have a regular high low high low + * pattern. */ + uint32_t minlen = 13; /* Min run of coherent samples. Up to + 12 samples it's very easy to mistake + noise for signal. */ + + ProtoViewMsgInfo *info = malloc(sizeof(ProtoViewMsgInfo)); + uint32_t i = 0; + + while (i < copy->total-1) { + uint32_t thislen = search_coherent_signal(copy,i); + + /* For messages that are long enough, attempt decoding. */ + if (thislen > minlen) { + initialize_msg_info(info); + uint32_t saved_idx = copy->idx; /* Save index, see later. */ + /* decode_signal() expects the detected signal to start + * from index .*/ + raw_samples_center(copy,i); + bool decoded = decode_signal(copy,thislen,info); + copy->idx = saved_idx; /* Restore the index as we are scanning + the signal in the loop. */ + + /* Accept this signal as the new signal if either it's longer + * than the previous one, or the previous one was unknown and + * this is decoded. */ + if (thislen > app->signal_bestlen || + (app->signal_decoded == false && decoded)) + { + app->signal_info = *info; + app->signal_bestlen = thislen; + app->signal_decoded = decoded; + raw_samples_copy(DetectedSamples,copy); + raw_samples_center(DetectedSamples,i); + FURI_LOG_E(TAG, "Displayed sample updated (%d samples %lu us)", + (int)thislen, DetectedSamples->short_pulse_dur); + } + } + i += thislen ? thislen : 1; + } + raw_samples_free(copy); + free(info); +} + +/* ============================================================================= + * Decoding + * + * The following code will translates the raw singals as received by + * the CC1101 into logical signals: a bitmap of 0s and 1s sampled at + * the detected data clock interval. + * + * Then the converted signal is passed to the protocols decoders, that look + * for protocol-specific information. We stop at the first decoder that is + * able to decode the data, so protocols here should be registered in + * order of complexity and specificity, with the generic ones at the end. + * ===========================================================================*/ + +/* Set the 'bitpos' bit to value 'val', in the specified bitmap + * 'b' of len 'blen'. + * Out of range bits will silently be discarded. */ +void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val) { + uint32_t byte = bitpos/8; + uint32_t bit = 7-(bitpos&7); + if (byte >= blen) return; + if (val) + b[byte] |= 1<= blen) return 0; + return (b[byte] & (1< rate/2) numbits++; /* There is another one. */ + + /* Limit how much a single sample can spawn. There are likely no + * protocols doing such long pulses when the rate is low. */ + if (numbits > 1024) numbits = 1024; + + if (0) /* Super verbose, so not under the DEBUG_MSG define. */ + FURI_LOG_E(TAG, "%lu converted into %lu (%d) bits", + dur,numbits,(int)level); + + /* If the signal is too short, let's claim it an interference + * and ignore it completely. */ + if (numbits == 0) continue; + + while(numbits--) bitmap_set(b,blen,bitpos++,level); + } + return bitpos; +} + +/* This function converts the line code used to the final data representation. + * The representation is put inside 'buf', for up to 'buflen' bytes of total + * data. For instance in order to convert manchester I can use "10" and "01" + * as zero and one patterns. It is possible to use "?" inside patterns in + * order to skip certain bits. For instance certain devices encode data twice, + * with each bit encoded in manchester encoding and then in its reversed + * representation. In such a case I could use "10??" and "01??". + * + * The function returns the number of bits converted. It will stop as soon + * as it finds a pattern that does not match zero or one patterns, or when + * the end of the bitmap pointed by 'bits' is reached (the length is + * specified in bytes by the caller, via the 'len' parameters). + * + * The decoding starts at the specified offset (in bits) 'off'. */ +uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, const char *zero_pattern, const char *one_pattern) +{ + uint32_t decoded = 0; /* Number of bits extracted. */ + len *= 8; /* Convert bytes to bits. */ + while(off < len) { + bool bitval; + if (bitmap_match_bits(bits,len,off,zero_pattern)) { + bitval = false; + off += strlen(zero_pattern); + } else if (bitmap_match_bits(bits,len,off,one_pattern)) { + bitval = true; + off += strlen(one_pattern); + } else { + break; + } + bitmap_set(buf,buflen,decoded++,bitval); + if (decoded/8 == buflen) break; /* No space left on target buffer. */ + } + return decoded; +} + +/* Supported protocols go here, with the relevant implementation inside + * protocols/.c */ + +extern ProtoViewDecoder Oregon2Decoder; +extern ProtoViewDecoder B4B1Decoder; +extern ProtoViewDecoder RenaultTPMSDecoder; + +ProtoViewDecoder *Decoders[] = { + &Oregon2Decoder, /* Oregon sensors v2.1 protocol. */ + &B4B1Decoder, /* PT, SC, ... 24 bits remotes. */ + &RenaultTPMSDecoder, /* Renault TPMS. */ + NULL +}; + +/* Reset the message info structure before passing it to the decoding + * functions. */ +void initialize_msg_info(ProtoViewMsgInfo *i) { + memset(i,0,sizeof(ProtoViewMsgInfo)); +} + +/* This function is called when a new signal is detected. It converts it + * to a bitstream, and the calls the protocol specific functions for + * decoding. If the signal was decoded correctly by some protocol, true + * is returned. Otherwise false is returned. */ +bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) { + uint32_t bitmap_bits_size = 4096*8; + uint32_t bitmap_size = bitmap_bits_size/8; + + /* We call the decoders with an offset a few bits before the actual + * signal detected and for a len of a few bits after its end. */ + uint32_t before_after_bits = 2; + + uint8_t *bitmap = malloc(bitmap_size); + uint32_t bits = convert_signal_to_bits(bitmap,bitmap_size,s,-before_after_bits,len+before_after_bits*2,s->short_pulse_dur); + + if (DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */ + char *str = malloc(1024); + uint32_t j; + for (j = 0; j < bits && j < 1023; j++) { + str[j] = bitmap_get(bitmap,bitmap_size,j) ? '1' : '0'; + } + str[j] = 0; + FURI_LOG_E(TAG, "%lu bits sampled: %s", bits, str); + free(str); + } + + /* Try all the decoders available. */ + int j = 0; + + bool decoded = false; + while(Decoders[j]) { + uint32_t start_time = furi_get_tick(); + decoded = Decoders[j]->decode(bitmap,bitmap_size,bits,info); + uint32_t delta = furi_get_tick() - start_time; + FURI_LOG_E(TAG, "Decoder %s took %lu ms", + Decoders[j]->name, (unsigned long)delta); + if (decoded) break; + j++; + } + + if (!decoded) { + FURI_LOG_E(TAG, "No decoding possible"); + } else { + FURI_LOG_E(TAG, "Decoded %s, raw=%s info=[%s,%s,%s]", info->name, info->raw, info->info1, info->info2, info->info3); + } + free(bitmap); + return decoded; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/ui.c b/Applications/Official/DEV_FW/source/xMasterX/protoview/ui.c new file mode 100644 index 000000000..e22e4d57e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/ui.c @@ -0,0 +1,30 @@ +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + +#include "app.h" + +void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color) +{ + struct { + uint8_t x; uint8_t y; + } dir[8] = { + {-1,-1}, + {0,-1}, + {1,-1}, + {1,0}, + {1,1}, + {0,1}, + {-1,1}, + {-1,0} + }; + + /* Rotate in all the directions writing the same string to create a + * border, then write the actual string in the other color in the + * middle. */ + canvas_set_color(canvas, border_color); + for (int j = 0; j < 8; j++) + canvas_draw_str(canvas,x+dir[j].x,y+dir[j].y,str); + canvas_set_color(canvas, text_color); + canvas_draw_str(canvas,x,y,str); + canvas_set_color(canvas, ColorBlack); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/view_info.c b/Applications/Official/DEV_FW/source/xMasterX/protoview/view_info.c new file mode 100644 index 000000000..fff9c836e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/view_info.c @@ -0,0 +1,41 @@ +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + +#include "app.h" + +/* Renders the view with the detected message information. */ +void render_view_info(Canvas *const canvas, ProtoViewApp *app) { + if (app->signal_decoded == false) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 30,36,"No signal decoded"); + return; + } + + /* Protocol name as title. */ + canvas_set_font(canvas, FontPrimary); + uint8_t y = 8, lineheight = 10; + canvas_draw_str(canvas, 0, y, app->signal_info.name); + y += lineheight; + + /* Info fields. */ + char buf[128]; + canvas_set_font(canvas, FontSecondary); + if (app->signal_info.raw[0]) { + snprintf(buf,sizeof(buf),"Raw: %s", app->signal_info.raw); + canvas_draw_str(canvas, 0, y, buf); + y += lineheight; + } + canvas_draw_str(canvas, 0, y, app->signal_info.info1); + y += lineheight; + canvas_draw_str(canvas, 0, y, app->signal_info.info2); + y += lineheight; + canvas_draw_str(canvas, 0, y, app->signal_info.info3); + y += lineheight; +} + +/* Handle input for the settings view. */ +void process_input_info(ProtoViewApp *app, InputEvent input) { + UNUSED(app); + UNUSED(input); + return; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/view_raw_signal.c b/Applications/Official/DEV_FW/source/xMasterX/protoview/view_raw_signal.c new file mode 100644 index 000000000..58d23e8ee --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/view_raw_signal.c @@ -0,0 +1,97 @@ +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + +#include "app.h" + +/* Render the received signal. + * + * The screen of the flipper is 128 x 64. Even using 4 pixels per line + * (where low level signal is one pixel high, high level is 4 pixels + * high) and 4 pixels of spacing between the different lines, we can + * plot comfortably 8 lines. + * + * The 'idx' argument is the first sample to render in the circular + * buffer. */ +void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *buf, uint32_t idx) { + canvas_set_color(canvas, ColorBlack); + + int rows = 8; + uint32_t time_per_pixel = app->us_scale; + uint32_t start_idx = idx; + bool level = 0; + uint32_t dur = 0, sample_num = 0; + for (int row = 0; row < rows ; row++) { + for (int x = 0; x < 128; x++) { + int y = 3 + row*8; + if (dur < time_per_pixel/2) { + /* Get more data. */ + raw_samples_get(buf, idx++, &level, &dur); + sample_num++; + } + + canvas_draw_line(canvas, x,y,x,y-(level*3)); + + /* Write a small triangle under the last sample detected. */ + if (app->signal_bestlen != 0 && + sample_num+start_idx == app->signal_bestlen+1) + { + canvas_draw_dot(canvas,x,y+2); + canvas_draw_dot(canvas,x-1,y+3); + canvas_draw_dot(canvas,x,y+3); + canvas_draw_dot(canvas,x+1,y+3); + sample_num++; /* Make sure we don't mark the next, too. */ + } + + /* Remove from the current level duration the time we + * just plot. */ + if (dur > time_per_pixel) + dur -= time_per_pixel; + else + dur = 0; + } + } +} + +/* Raw pulses rendering. This is our default view. */ +void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app) { + /* Show signal. */ + render_signal(app, canvas, DetectedSamples, app->signal_offset); + + /* Show signal information. */ + char buf[64]; + snprintf(buf,sizeof(buf),"%luus", + (unsigned long)DetectedSamples->short_pulse_dur); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_with_border(canvas, 97, 63, buf, ColorWhite, ColorBlack); + if (app->signal_decoded) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_with_border(canvas, 1, 61, app->signal_info.name, ColorWhite, ColorBlack); + } +} + +/* Handle input for the raw pulses view. */ +void process_input_raw_pulses(ProtoViewApp *app, InputEvent input) { + if (input.type == InputTypeRepeat) { + /* Handle panning of the signal window. Long pressing + * right will show successive samples, long pressing left + * previous samples. */ + if (input.key == InputKeyRight) app->signal_offset++; + else if (input.key == InputKeyLeft) app->signal_offset--; + else if (input.key == InputKeyOk) { + app->signal_offset = 0; + app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE; + } + } else if (input.type == InputTypeShort) { + if (input.key == InputKeyOk) { + /* Reset the current sample to capture the next. */ + reset_current_signal(app); + } else if (input.key == InputKeyDown) { + /* Rescaling. The set becomes finer under 50us per pixel. */ + uint32_t scale_step = app->us_scale >= 50 ? 50 : 10; + if (app->us_scale < 500) app->us_scale += scale_step; + } else if (input.key == InputKeyUp) { + uint32_t scale_step = app->us_scale > 50 ? 50 : 10; + if (app->us_scale > 10) app->us_scale -= scale_step; + } + } +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/protoview/view_settings.c b/Applications/Official/DEV_FW/source/xMasterX/protoview/view_settings.c new file mode 100644 index 000000000..3a06e61b3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/protoview/view_settings.c @@ -0,0 +1,93 @@ +/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved + * See the LICENSE file for information about the license. */ + +#include "app.h" + +/* Renders a single view with frequency and modulation setting. However + * this are logically two different views, and only one of the settings + * will be highlighted. */ +void render_view_settings(Canvas *const canvas, ProtoViewApp *app) { + canvas_set_font(canvas, FontPrimary); + if (app->current_view == ViewFrequencySettings) + canvas_draw_str_with_border(canvas,1,10,"Frequency",ColorWhite,ColorBlack); + else + canvas_draw_str(canvas,1,10,"Frequency"); + + if (app->current_view == ViewModulationSettings) + canvas_draw_str_with_border(canvas,70,10,"Modulation",ColorWhite,ColorBlack); + else + canvas_draw_str(canvas,70,10,"Modulation"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas,10,61,"Use up and down to modify"); + + /* Show frequency. We can use big numbers font since it's just a number. */ + if (app->current_view == ViewFrequencySettings) { + char buf[16]; + snprintf(buf,sizeof(buf),"%.2f",(double)app->frequency/1000000); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str(canvas, 30, 40, buf); + } else if (app->current_view == ViewModulationSettings) { + int current = app->modulation; + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 33, 39, ProtoViewModulations[current].name); + } +} + +/* Handle input for the settings view. */ +void process_input_settings(ProtoViewApp *app, InputEvent input) { + if (input.type == InputTypeLong && input.key == InputKeyOk) { + /* Long pressing to OK sets the default frequency and + * modulation. */ + app->frequency = subghz_setting_get_default_frequency(app->setting); + app->modulation = 0; + } else if (input.type == InputTypePress && + (input.key != InputKeyDown || input.key != InputKeyUp)) + { + /* Handle up and down to change frequency or modulation. */ + if (app->current_view == ViewFrequencySettings) { + size_t curidx = 0, i; + size_t count = subghz_setting_get_frequency_count(app->setting); + + /* Scan the list of frequencies to check for the index of the + * currently set frequency. */ + for(i = 0; i < count; i++) { + uint32_t freq = subghz_setting_get_frequency(app->setting,i); + if (freq == app->frequency) { + curidx = i; + break; + } + } + if (i == count) return; /* Should never happen. */ + + if (input.key == InputKeyUp) { + curidx = (curidx+1) % count; + } else if (input.key == InputKeyDown) { + curidx = curidx == 0 ? count-1 : curidx-1; + } else { + return; + } + app->frequency = subghz_setting_get_frequency(app->setting,curidx); + } else if (app->current_view == ViewModulationSettings) { + uint32_t count = 0; + uint32_t modid = app->modulation; + + while(ProtoViewModulations[count].name != NULL) count++; + if (input.key == InputKeyUp) { + modid = (modid+1) % count; + } else if (input.key == InputKeyDown) { + modid = modid == 0 ? count-1 : modid-1; + } else { + return; + } + app->modulation = modid; + } + } else { + return; + } + + /* Apply changes. */ + FURI_LOG_E(TAG, "Setting view, setting frequency/modulation to %lu %s", app->frequency, ProtoViewModulations[app->modulation].name); + radio_rx_end(app); + radio_begin(app); + radio_rx(app); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/.gitignore b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/.gitignore new file mode 100644 index 000000000..c6127b38c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/README.md b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/README.md new file mode 100644 index 000000000..0667860e7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/README.md @@ -0,0 +1,38 @@ +# RC2014 ColecoVision Controller for Flipper Zero + +A Flipper Zero application and [RC2014] module allowing the Flipper to be used as a controller for ColecoVision games on +the [RC2014]. + +![ui](ui.png) + +## Running ColecoVision Games on the RC2014 + +A full tutorial is out of scope here, but briefly, you will need a [RC2014] with J. B. Langston's [TMS9918A Video Card] +and [SN76489 Sound Card], as well as some way to launch ColecoVision ROMs. + +Note that if you're using the standard pageable ROM module (e.g. if you're using the stock Pro kit), you will need to +[modify it](https://github.com/jblang/TMS9918A/issues/12) in order for the TMS9918A module to work on the ColecoVision +port addresses. + +## Hardware Setup + +The [interface](interface) directory contains Eagle schematics for a RC2014 module that handles the controller port +addressing for two players, breaking out the 8 data line inputs as well as the mode select line. This can actually be +used for different controller implementations and is slightly more flexible than the actual [ColecoVision] spec. + +To use this with the Flipper Zero and this application, a GPIO board is needed to provide hardware multiplexing for the +data lines. A schematic for the GPIO board will be added to this repository soon. + +## Building the FAP + +1. Clone the [flipperzero-firmware] repository. +2. Create a symbolic link in `applications_user` named `coleco`, pointing to this repository. +3. Compile with `./fbt fap_coleco`. +4. Copy `build/f7-firmware-D/.extapps/coleco.fap` to `apps/Misc` on the SD card (directly or using [qFlipper]). + +[RC2014]: https://rc2014.co.uk/ +[TMS9918A Video Card]: https://github.com/jblang/TMS9918A +[SN76489 Sound Card]: https://github.com/jblang/SN76489 +[ColecoVision]: http://www.atarihq.com/danb/files/CV-Tech.txt +[flipperzero-firmware]: https://github.com/flipperdevices/flipperzero-firmware +[qFlipper]: https://flipperzero.one/update diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/application.fam b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/application.fam new file mode 100644 index 000000000..105797865 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/application.fam @@ -0,0 +1,13 @@ +App( + appid="coleco", + name="RC2014 ColecoVision", + apptype=FlipperAppType.EXTERNAL, + entry_point="coleco_app", + cdefines=["APP_COLECO"], + requires=["gui"], + stack_size=1 * 1024, + order=35, + fap_icon="coleco_10px.png", + fap_icon_assets="icons", + fap_category="GPIO_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/coleco.c b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/coleco.c new file mode 100644 index 000000000..2ba78858d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/coleco.c @@ -0,0 +1,366 @@ +#include +#include +#include +#include +#include "coleco_icons.h" + +#define CODE_0 0x0A +#define CODE_1 0x0D +#define CODE_2 0x07 +#define CODE_3 0x0C +#define CODE_4 0x02 +#define CODE_5 0x03 +#define CODE_6 0x0E +#define CODE_7 0x05 +#define CODE_8 0x01 +#define CODE_9 0x0B +#define CODE_H 0x06 +#define CODE_S 0x09 +#define CODE_N 0x0F + +const GpioPin* const pin_up = &gpio_ext_pa6; +const GpioPin* const pin_down = &gpio_ext_pc0; +const GpioPin* const pin_right = &gpio_ext_pb2; +const GpioPin* const pin_left = &gpio_ext_pc3; +const GpioPin* const pin_code0 = &gpio_ext_pa7; +const GpioPin* const pin_code1 = &gpio_ext_pa4; +const GpioPin* const pin_code2 = &ibutton_gpio; +const GpioPin* const pin_code3 = &gpio_ext_pc1; +const GpioPin* const pin_fire = &gpio_ext_pb3; +const GpioPin* const pin_alt = &gpio_usart_tx; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + bool dpad; + int row; + int column; +} Coleco; + +static void render_callback(Canvas* const canvas, void* context) { + Coleco* coleco = acquire_mutex((ValueMutex*)context, 25); + if(coleco == NULL) { + return; + } + + if(coleco->dpad) { + canvas_draw_icon(canvas, 4, 16, &I_ColecoJoystick_sel_33x33); + canvas_draw_icon(canvas, 27, 52, &I_ColecoFire_sel_18x9); + } else { + const bool hvr = coleco->row == 0 && coleco->column < 2; + canvas_draw_icon( + canvas, 4, 16, hvr ? &I_ColecoJoystick_hvr_33x33 : &I_ColecoJoystick_33x33); + canvas_draw_icon(canvas, 27, 52, hvr ? &I_ColecoFire_hvr_18x9 : &I_ColecoFire_18x9); + } + + canvas_draw_icon( + canvas, + 27, + 4, + (coleco->row == 0 && coleco->column == 2) ? &I_ColecoAlt_hvr_18x9 : &I_ColecoAlt_18x9); + canvas_draw_icon( + canvas, + 49, + 44, + (coleco->row == 1 && coleco->column == 0) ? &I_Coleco1_hvr_17x17 : &I_Coleco1_17x17); + canvas_draw_icon( + canvas, + 49, + 24, + (coleco->row == 1 && coleco->column == 1) ? &I_Coleco2_hvr_17x17 : &I_Coleco2_17x17); + canvas_draw_icon( + canvas, + 49, + 4, + (coleco->row == 1 && coleco->column == 2) ? &I_Coleco3_hvr_17x17 : &I_Coleco3_17x17); + canvas_draw_icon( + canvas, + 69, + 44, + (coleco->row == 2 && coleco->column == 0) ? &I_Coleco4_hvr_17x17 : &I_Coleco4_17x17); + canvas_draw_icon( + canvas, + 69, + 24, + (coleco->row == 2 && coleco->column == 1) ? &I_Coleco5_hvr_17x17 : &I_Coleco5_17x17); + canvas_draw_icon( + canvas, + 69, + 4, + (coleco->row == 2 && coleco->column == 2) ? &I_Coleco6_hvr_17x17 : &I_Coleco6_17x17); + canvas_draw_icon( + canvas, + 89, + 44, + (coleco->row == 3 && coleco->column == 0) ? &I_Coleco7_hvr_17x17 : &I_Coleco7_17x17); + canvas_draw_icon( + canvas, + 89, + 24, + (coleco->row == 3 && coleco->column == 1) ? &I_Coleco8_hvr_17x17 : &I_Coleco8_17x17); + canvas_draw_icon( + canvas, + 89, + 4, + (coleco->row == 3 && coleco->column == 2) ? &I_Coleco9_hvr_17x17 : &I_Coleco9_17x17); + canvas_draw_icon( + canvas, + 109, + 44, + (coleco->row == 4 && coleco->column == 0) ? &I_ColecoStar_hvr_17x17 : &I_ColecoStar_17x17); + canvas_draw_icon( + canvas, + 109, + 24, + (coleco->row == 4 && coleco->column == 1) ? &I_Coleco0_hvr_17x17 : &I_Coleco0_17x17); + canvas_draw_icon( + canvas, + 109, + 4, + (coleco->row == 4 && coleco->column == 2) ? &I_ColecoPound_hvr_17x17 : + &I_ColecoPound_17x17); + + release_mutex((ValueMutex*)context, coleco); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void coleco_write_code(uint8_t code) { + furi_hal_gpio_write(pin_code0, (code & 1)); + furi_hal_gpio_write(pin_code1, (code & 2)); + furi_hal_gpio_write(pin_code2, (code & 4)); + furi_hal_gpio_write(pin_code3, (code & 8)); +} + +static void coleco_gpio_init() { + // configure output pins + furi_hal_gpio_init(pin_up, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(pin_down, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(pin_right, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(pin_left, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(pin_code0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(pin_code1, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(pin_code2, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(pin_code3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(pin_fire, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(pin_alt, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + furi_hal_gpio_write(pin_up, true); + furi_hal_gpio_write(pin_down, true); + furi_hal_gpio_write(pin_right, true); + furi_hal_gpio_write(pin_left, true); + furi_hal_gpio_write(pin_fire, true); + furi_hal_gpio_write(pin_alt, true); + + coleco_write_code(CODE_N); +} + +static Coleco* coleco_alloc() { + Coleco* coleco = malloc(sizeof(Coleco)); + + coleco->dpad = false; + coleco->row = 0; + coleco->column = 1; + + return coleco; +} + +static void coleco_free(Coleco* coleco) { + furi_assert(coleco); + + free(coleco); +} + +int32_t coleco_app(void* p) { + UNUSED(p); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + Coleco* coleco = coleco_alloc(); + + ValueMutex coleco_mutex; + if(!init_mutex(&coleco_mutex, coleco, sizeof(Coleco))) { + FURI_LOG_E("Coleco", "cannot create mutex\r\n"); + coleco_free(coleco); + return 255; + } + + // set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &coleco_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + coleco_gpio_init(); + furi_hal_power_enable_otg(); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + + Coleco* coleco = (Coleco*)acquire_mutex_block(&coleco_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + switch(event.input.key) { + case InputKeyUp: + if(coleco->dpad) { + if(event.input.type == InputTypePress) { + furi_hal_gpio_write(pin_up, false); + } else if(event.input.type == InputTypeRelease) { + furi_hal_gpio_write(pin_up, true); + } + } else { + if(event.input.type == InputTypePress && coleco->column < 2) { + coleco->column++; + coleco_write_code(CODE_N); + } + } + break; + case InputKeyDown: + if(coleco->dpad) { + if(event.input.type == InputTypePress) { + furi_hal_gpio_write(pin_down, false); + } else if(event.input.type == InputTypeRelease) { + furi_hal_gpio_write(pin_down, true); + } + } else { + if(event.input.type == InputTypePress && coleco->column > 0) { + coleco->column--; + coleco_write_code(CODE_N); + } + } + break; + case InputKeyRight: + if(coleco->dpad) { + if(event.input.type == InputTypePress) { + furi_hal_gpio_write(pin_right, false); + } else if(event.input.type == InputTypeRelease) { + furi_hal_gpio_write(pin_right, true); + } + } else { + if(event.input.type == InputTypePress && coleco->row < 4) { + coleco->row++; + coleco_write_code(CODE_N); + } + } + break; + case InputKeyLeft: + if(coleco->dpad) { + if(event.input.type == InputTypePress) { + furi_hal_gpio_write(pin_left, false); + } else if(event.input.type == InputTypeRelease) { + furi_hal_gpio_write(pin_left, true); + } + } else { + if(event.input.type == InputTypePress && coleco->row > 0) { + coleco->row--; + coleco_write_code(CODE_N); + } + } + break; + case InputKeyOk: + if(coleco->dpad) { + if(event.input.type == InputTypePress) { + furi_hal_gpio_write(pin_fire, false); + } else if(event.input.type == InputTypeRelease) { + furi_hal_gpio_write(pin_fire, true); + } + } else { + if(event.input.type == InputTypePress) { + if(coleco->row == 0) { + if(coleco->column == 2) { + furi_hal_gpio_write(pin_alt, false); + } else { + coleco->dpad = true; + } + } else if(coleco->row == 1) { + if(coleco->column == 0) { + coleco_write_code(CODE_1); + } else if(coleco->column == 1) { + coleco_write_code(CODE_2); + } else { + coleco_write_code(CODE_3); + } + } else if(coleco->row == 2) { + if(coleco->column == 0) { + coleco_write_code(CODE_4); + } else if(coleco->column == 1) { + coleco_write_code(CODE_5); + } else { + coleco_write_code(CODE_6); + } + } else if(coleco->row == 3) { + if(coleco->column == 0) { + coleco_write_code(CODE_7); + } else if(coleco->column == 1) { + coleco_write_code(CODE_8); + } else { + coleco_write_code(CODE_9); + } + } else if(coleco->row == 4) { + if(coleco->column == 0) { + coleco_write_code(CODE_S); + } else if(coleco->column == 1) { + coleco_write_code(CODE_0); + } else { + coleco_write_code(CODE_H); + } + } + } + if(event.input.type == InputTypeRelease) { + furi_hal_gpio_write(pin_alt, true); + coleco_write_code(CODE_N); + } + } + break; + case InputKeyBack: + if(event.input.type == InputTypePress) { + if(coleco->dpad) { + coleco->dpad = false; + } else { + processing = false; + } + } + break; + default: + break; + } + + view_port_update(view_port); + } + } else { + FURI_LOG_D("Coleco", "FuriMessageQueue: event timeout"); + } + + release_mutex(&coleco_mutex, coleco); + } + + furi_hal_power_disable_otg(); + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&coleco_mutex); + coleco_free(coleco); + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/coleco_10px.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/coleco_10px.png new file mode 100644 index 000000000..d51652adc Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/coleco_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco0_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco0_17x17.png new file mode 100644 index 000000000..b53bc3b5f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco0_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco0_hvr_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco0_hvr_17x17.png new file mode 100644 index 000000000..19627388e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco0_hvr_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco1_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco1_17x17.png new file mode 100644 index 000000000..2c3977967 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco1_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco1_hvr_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco1_hvr_17x17.png new file mode 100644 index 000000000..562c7e8db Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco1_hvr_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco2_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco2_17x17.png new file mode 100644 index 000000000..f8f18405f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco2_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco2_hvr_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco2_hvr_17x17.png new file mode 100644 index 000000000..cac468981 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco2_hvr_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco3_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco3_17x17.png new file mode 100644 index 000000000..3f2288392 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco3_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco3_hvr_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco3_hvr_17x17.png new file mode 100644 index 000000000..c0015312a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco3_hvr_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco4_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco4_17x17.png new file mode 100644 index 000000000..b3888910c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco4_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco4_hvr_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco4_hvr_17x17.png new file mode 100644 index 000000000..63e086275 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco4_hvr_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco5_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco5_17x17.png new file mode 100644 index 000000000..42eb3f59d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco5_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco5_hvr_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco5_hvr_17x17.png new file mode 100644 index 000000000..b06fdfaf9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco5_hvr_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco6_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco6_17x17.png new file mode 100644 index 000000000..6ed3e239c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco6_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco6_hvr_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco6_hvr_17x17.png new file mode 100644 index 000000000..4be93b365 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco6_hvr_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco7_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco7_17x17.png new file mode 100644 index 000000000..2d200d71b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco7_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco7_hvr_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco7_hvr_17x17.png new file mode 100644 index 000000000..8886dffef Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco7_hvr_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco8_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco8_17x17.png new file mode 100644 index 000000000..3905ef80d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco8_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco8_hvr_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco8_hvr_17x17.png new file mode 100644 index 000000000..519ac1e97 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco8_hvr_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco9_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco9_17x17.png new file mode 100644 index 000000000..a51739a1b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco9_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco9_hvr_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco9_hvr_17x17.png new file mode 100644 index 000000000..206e0acd9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/Coleco9_hvr_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoAlt_18x9.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoAlt_18x9.png new file mode 100644 index 000000000..7e6853e52 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoAlt_18x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoAlt_hvr_18x9.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoAlt_hvr_18x9.png new file mode 100644 index 000000000..6b15dcf7b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoAlt_hvr_18x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoFire_18x9.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoFire_18x9.png new file mode 100644 index 000000000..8be499c21 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoFire_18x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoFire_hvr_18x9.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoFire_hvr_18x9.png new file mode 100644 index 000000000..2b0d1d72c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoFire_hvr_18x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoFire_sel_18x9.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoFire_sel_18x9.png new file mode 100644 index 000000000..383cd3a2d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoFire_sel_18x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoJoystick_33x33.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoJoystick_33x33.png new file mode 100644 index 000000000..de4c574bc Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoJoystick_33x33.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoJoystick_hvr_33x33.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoJoystick_hvr_33x33.png new file mode 100644 index 000000000..fd653bfaf Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoJoystick_hvr_33x33.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoJoystick_sel_33x33.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoJoystick_sel_33x33.png new file mode 100644 index 000000000..ea01af395 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoJoystick_sel_33x33.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoPound_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoPound_17x17.png new file mode 100644 index 000000000..10b46e0ca Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoPound_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoPound_hvr_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoPound_hvr_17x17.png new file mode 100644 index 000000000..784f3687c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoPound_hvr_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoStar_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoStar_17x17.png new file mode 100644 index 000000000..3031c0baf Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoStar_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoStar_hvr_17x17.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoStar_hvr_17x17.png new file mode 100644 index 000000000..546922971 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/icons/ColecoStar_hvr_17x17.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/interface/flipper-coleco.brd b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/interface/flipper-coleco.brd new file mode 100644 index 000000000..47ed27322 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/interface/flipper-coleco.brd @@ -0,0 +1,2554 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +COLECOVISION +INTERFACE +PLAYER 1 +PLAYER 2 +74HCT138 +74HCT00 +74HCT541 +74HCT541 + + + +<b>TTL Devices, 74xx Series with US Symbols</b><p> +Based on the following sources: +<ul> +<li>Texas Instruments <i>TTL Data Book</i>&nbsp;&nbsp;&nbsp;Volume 1, 1996. +<li>TTL Data Book, Volume 2 , 1993 +<li>National Seminconductor Databook 1990, ALS/LS Logic +<li>ttl 74er digital data dictionary, ECA Electronic + Acustic GmbH, ISBN 3-88109-032-0 +<li>http://icmaster.com/ViewCompare.asp +</ul> +<author>Created by librarian@cadsoft.de</author> + + +<b>Dual In Line Package</b> + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>Dual In Line Package</b> + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>Dual In Line Package</b> + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>Resistors, Capacitors, Inductors</b><p> +Based on the previous libraries: +<ul> +<li>r.lbr +<li>cap.lbr +<li>cap-fe.lbr +<li>captant.lbr +<li>polcap.lbr +<li>ipc-smd.lbr +</ul> +All SMD packages are defined according to the IPC specifications and CECC<p> +<author>Created by librarian@cadsoft.de</author><p> +<p> +for Electrolyt Capacitors see also :<p> +www.bccomponents.com <p> +www.panasonic.com<p> +www.kemet.com<p> +http://www.secc.co.jp/pdf/os_e/2004/e_os_all.pdf <b>(SANYO)</b> +<p> +for trimmer refence see : <u>www.electrospec-inc.com/cross_references/trimpotcrossref.asp</u><p> + +<table border=0 cellspacing=0 cellpadding=0 width="100%" cellpaddding=0> +<tr valign="top"> + +<! <td width="10">&nbsp;</td> +<td width="90%"> + +<b><font color="#0000FF" size="4">TRIM-POT CROSS REFERENCE</font></b> +<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=2> + <TR> + <TD COLSPAN=8> + <FONT SIZE=3 FACE=ARIAL><B>RECTANGULAR MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BOURNS</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BI&nbsp;TECH</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">DALE-VISHAY</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PHILIPS/MEPCO</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MURATA</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PANASONIC</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">SPECTROL</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MILSPEC</FONT> + </B> + </TD><TD>&nbsp;</TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3 > + 3005P<BR> + 3006P<BR> + 3006W<BR> + 3006Y<BR> + 3009P<BR> + 3009W<BR> + 3009Y<BR> + 3057J<BR> + 3057L<BR> + 3057P<BR> + 3057Y<BR> + 3059J<BR> + 3059L<BR> + 3059P<BR> + 3059Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 89P<BR> + 89W<BR> + 89X<BR> + 89PH<BR> + 76P<BR> + 89XH<BR> + 78SLT<BR> + 78L&nbsp;ALT<BR> + 56P&nbsp;ALT<BR> + 78P&nbsp;ALT<BR> + T8S<BR> + 78L<BR> + 56P<BR> + 78P<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + T18/784<BR> + 783<BR> + 781<BR> + -<BR> + -<BR> + -<BR> + 2199<BR> + 1697/1897<BR> + 1680/1880<BR> + 2187<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 8035EKP/CT20/RJ-20P<BR> + -<BR> + RJ-20X<BR> + -<BR> + -<BR> + -<BR> + 1211L<BR> + 8012EKQ&nbsp;ALT<BR> + 8012EKR&nbsp;ALT<BR> + 1211P<BR> + 8012EKJ<BR> + 8012EKL<BR> + 8012EKQ<BR> + 8012EKR<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 2101P<BR> + 2101W<BR> + 2101Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 2102L<BR> + 2102S<BR> + 2102Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVMCOG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 43P<BR> + 43W<BR> + 43Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 40L<BR> + 40P<BR> + 40Y<BR> + 70Y-T602<BR> + 70L<BR> + 70P<BR> + 70Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + RT/RTR12<BR> + RT/RTR12<BR> + RT/RTR12<BR> + -<BR> + RJ/RJR12<BR> + RJ/RJR12<BR> + RJ/RJR12<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SQUARE MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3250L<BR> + 3250P<BR> + 3250W<BR> + 3250X<BR> + 3252P<BR> + 3252W<BR> + 3252X<BR> + 3260P<BR> + 3260W<BR> + 3260X<BR> + 3262P<BR> + 3262W<BR> + 3262X<BR> + 3266P<BR> + 3266W<BR> + 3266X<BR> + 3290H<BR> + 3290P<BR> + 3290W<BR> + 3292P<BR> + 3292W<BR> + 3292X<BR> + 3296P<BR> + 3296W<BR> + 3296X<BR> + 3296Y<BR> + 3296Z<BR> + 3299P<BR> + 3299W<BR> + 3299X<BR> + 3299Y<BR> + 3299Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + -<BR> + 64W&nbsp;ALT<BR> + -<BR> + 64P&nbsp;ALT<BR> + 64W&nbsp;ALT<BR> + 64X&nbsp;ALT<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66P<BR> + 66W<BR> + 66X<BR> + 67P<BR> + 67W<BR> + 67X<BR> + 67Y<BR> + 67Z<BR> + 68P<BR> + 68W<BR> + 68X<BR> + 67Y&nbsp;ALT<BR> + 67Z&nbsp;ALT<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 5050<BR> + 5091<BR> + 5080<BR> + 5087<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + T63YB<BR> + T63XB<BR> + -<BR> + -<BR> + -<BR> + 5887<BR> + 5891<BR> + 5880<BR> + -<BR> + -<BR> + -<BR> + T93Z<BR> + T93YA<BR> + T93XA<BR> + T93YB<BR> + T93XB<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 8026EKP<BR> + 8026EKW<BR> + 8026EKM<BR> + 8026EKP<BR> + 8026EKB<BR> + 8026EKM<BR> + 1309X<BR> + 1309P<BR> + 1309W<BR> + 8024EKP<BR> + 8024EKW<BR> + 8024EKN<BR> + RJ-9P/CT9P<BR> + RJ-9W<BR> + RJ-9X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3105P/3106P<BR> + 3105W/3106W<BR> + 3105X/3106X<BR> + 3105Y/3106Y<BR> + 3105Z/3105Z<BR> + 3102P<BR> + 3102W<BR> + 3102X<BR> + 3102Y<BR> + 3102Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMCBG<BR> + EVMCCG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 55-1-X<BR> + 55-4-X<BR> + 55-3-X<BR> + 55-2-X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 50-2-X<BR> + 50-4-X<BR> + 50-3-X<BR> + -<BR> + -<BR> + -<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 64Y<BR> + 64Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3323P<BR> + 3323S<BR> + 3323W<BR> + 3329H<BR> + 3329P<BR> + 3329W<BR> + 3339H<BR> + 3339P<BR> + 3339W<BR> + 3352E<BR> + 3352H<BR> + 3352K<BR> + 3352P<BR> + 3352T<BR> + 3352V<BR> + 3352W<BR> + 3362H<BR> + 3362M<BR> + 3362P<BR> + 3362R<BR> + 3362S<BR> + 3362U<BR> + 3362W<BR> + 3362X<BR> + 3386B<BR> + 3386C<BR> + 3386F<BR> + 3386H<BR> + 3386K<BR> + 3386M<BR> + 3386P<BR> + 3386S<BR> + 3386W<BR> + 3386X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 25P<BR> + 25S<BR> + 25RX<BR> + 82P<BR> + 82M<BR> + 82PA<BR> + -<BR> + -<BR> + -<BR> + 91E<BR> + 91X<BR> + 91T<BR> + 91B<BR> + 91A<BR> + 91V<BR> + 91W<BR> + 25W<BR> + 25V<BR> + 25P<BR> + -<BR> + 25S<BR> + 25U<BR> + 25RX<BR> + 25X<BR> + 72XW<BR> + 72XL<BR> + 72PM<BR> + 72RX<BR> + -<BR> + 72PX<BR> + 72P<BR> + 72RXW<BR> + 72RXL<BR> + 72X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + T7YB<BR> + T7YA<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + TXD<BR> + TYA<BR> + TYP<BR> + -<BR> + TYD<BR> + TX<BR> + -<BR> + 150SX<BR> + 100SX<BR> + 102T<BR> + 101S<BR> + 190T<BR> + 150TX<BR> + 101<BR> + -<BR> + -<BR> + 101SX<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ET6P<BR> + ET6S<BR> + ET6X<BR> + RJ-6W/8014EMW<BR> + RJ-6P/8014EMP<BR> + RJ-6X/8014EMX<BR> + TM7W<BR> + TM7P<BR> + TM7X<BR> + -<BR> + 8017SMS<BR> + -<BR> + 8017SMB<BR> + 8017SMA<BR> + -<BR> + -<BR> + CT-6W<BR> + CT-6H<BR> + CT-6P<BR> + CT-6R<BR> + -<BR> + CT-6V<BR> + CT-6X<BR> + -<BR> + -<BR> + 8038EKV<BR> + -<BR> + 8038EKX<BR> + -<BR> + -<BR> + 8038EKP<BR> + 8038EKZ<BR> + 8038EKW<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 3321H<BR> + 3321P<BR> + 3321N<BR> + 1102H<BR> + 1102P<BR> + 1102T<BR> + RVA0911V304A<BR> + -<BR> + RVA0911H413A<BR> + RVG0707V100A<BR> + RVA0607V(H)306A<BR> + RVA1214H213A<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3104B<BR> + 3104C<BR> + 3104F<BR> + 3104H<BR> + -<BR> + 3104M<BR> + 3104P<BR> + 3104S<BR> + 3104W<BR> + 3104X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + EVMQ0G<BR> + EVMQIG<BR> + EVMQ3G<BR> + EVMS0G<BR> + EVMQ0G<BR> + EVMG0G<BR> + -<BR> + -<BR> + -<BR> + EVMK4GA00B<BR> + EVM30GA00B<BR> + EVMK0GA00B<BR> + EVM38GA00B<BR> + EVMB6<BR> + EVLQ0<BR> + -<BR> + EVMMSG<BR> + EVMMBG<BR> + EVMMAG<BR> + -<BR> + -<BR> + EVMMCS<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMM1<BR> + -<BR> + -<BR> + EVMM0<BR> + -<BR> + -<BR> + EVMM3<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 62-3-1<BR> + 62-1-2<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67R<BR> + -<BR> + 67P<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67X<BR> + 63V<BR> + 63S<BR> + 63M<BR> + -<BR> + -<BR> + 63H<BR> + 63P<BR> + -<BR> + -<BR> + 63X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P>&nbsp;<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=3> + <TR> + <TD COLSPAN=7> + <FONT color="#0000FF" SIZE=4 FACE=ARIAL><B>SMD TRIM-POT CROSS REFERENCE</B></FONT> + <P> + <FONT SIZE=4 FACE=ARIAL><B>MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3224G<BR> + 3224J<BR> + 3224W<BR> + 3269P<BR> + 3269W<BR> + 3269X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 44G<BR> + 44J<BR> + 44W<BR> + 84P<BR> + 84W<BR> + 84X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST63Z<BR> + ST63Y<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST5P<BR> + ST5W<BR> + ST5X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=7>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=7> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3314G<BR> + 3314J<BR> + 3364A/B<BR> + 3364C/D<BR> + 3364W/X<BR> + 3313G<BR> + 3313J<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 23B<BR> + 23A<BR> + 21X<BR> + 21W<BR> + -<BR> + 22B<BR> + 22A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST5YL/ST53YL<BR> + ST5YJ/5T53YJ<BR> + ST-23A<BR> + ST-22B<BR> + ST-22<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST-4B<BR> + ST-4A<BR> + -<BR> + -<BR> + -<BR> + ST-3B<BR> + ST-3A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVM-6YS<BR> + EVM-1E<BR> + EVM-1G<BR> + EVM-1D<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + G4B<BR> + G4A<BR> + TR04-3S1<BR> + TRG04-2S1<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + DVR-43A<BR> + CVR-42C<BR> + CVR-42A/C<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P> +<FONT SIZE=4 FACE=ARIAL><B>ALT =&nbsp;ALTERNATE</B></FONT> +<P> + +&nbsp; +<P> +</td> +</tr> +</table> + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 2.4 x 4.4 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + +<b>Pin Header Connectors</b><p> +<author>Created by librarian@cadsoft.de</author> + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + +<b>EAGLE Design Rules</b> +<p> +Die Standard-Design-Rules sind so gewählt, dass sie für +die meisten Anwendungen passen. Sollte ihre Platine +besondere Anforderungen haben, treffen Sie die erforderlichen +Einstellungen hier und speichern die Design Rules unter +einem neuen Namen ab. +<b>Laen's PCB Order Design Rules</b> +<p> +Please make sure your boards conform to these design rules. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Since Version 6.2.2 text objects can contain more than one line, +which will not be processed correctly with this version. + + + diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/interface/flipper-coleco.sch b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/interface/flipper-coleco.sch new file mode 100644 index 000000000..c29acd315 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/interface/flipper-coleco.sch @@ -0,0 +1,5482 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>TTL Devices, 74xx Series with US Symbols</b><p> +Based on the following sources: +<ul> +<li>Texas Instruments <i>TTL Data Book</i>&nbsp;&nbsp;&nbsp;Volume 1, 1996. +<li>TTL Data Book, Volume 2 , 1993 +<li>National Seminconductor Databook 1990, ALS/LS Logic +<li>ttl 74er digital data dictionary, ECA Electronic + Acustic GmbH, ISBN 3-88109-032-0 +<li>http://icmaster.com/ViewCompare.asp +</ul> +<author>Created by librarian@cadsoft.de</author> + + +<b>Dual In Line Package</b> + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>Wide Small Outline package</b> 300 mil + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>VALUE +>NAME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>Leadless Chip Carrier</b><p> Ceramic Package + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>Dual In Line Package</b> + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>Small Outline package</b> 150 mil + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>VALUE +>NAME + + + + + + + + + + + + + + + + + + +<b>Dual In Line Package</b> + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>Small Outline package</b> 150 mil + + + + + + + + + + + + + + + + + + + + + + + + + + +>VALUE +>NAME + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + +>NAME +GND +VCC + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + +Octal <b>BUFFER</b> and <b>LINE DRIVER</b>, 3-state + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +3-line to 8-line <b>DECODER/DEMULTIPLEXER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Quad 2-input <b>NAND</b> gate + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A15 +A0 +M1 +RST +CLK +INT +MREQ +WR +RO +IORQ +D0 +D7 +TX +RX + + +>NAME +GND +VCC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>Pin Header Connectors</b><p> +<author>Created by librarian@cadsoft.de</author> + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>Supply Symbols</b><p> + GND, VCC, 0V, +5V, -5V, etc.<p> + Please keep in mind, that these devices are necessary for the + automatic wiring of the supply signals.<p> + The pin name defined in the symbol is identical to the net which is to be wired automatically.<p> + In this library the device names are the same as the pin names of the symbols, therefore the correct signal names appear next to the supply symbols in the schematic.<p> + <author>Created by librarian@cadsoft.de</author> + + + + + +>VALUE + + + + + +>VALUE + + + + + +<b>SUPPLY SYMBOL</b> + + + + + + + + + + + + +<b>SUPPLY SYMBOL</b> + + + + + + + + + + + + + + +<b>Resistors, Capacitors, Inductors</b><p> +Based on the previous libraries: +<ul> +<li>r.lbr +<li>cap.lbr +<li>cap-fe.lbr +<li>captant.lbr +<li>polcap.lbr +<li>ipc-smd.lbr +</ul> +All SMD packages are defined according to the IPC specifications and CECC<p> +<author>Created by librarian@cadsoft.de</author><p> +<p> +for Electrolyt Capacitors see also :<p> +www.bccomponents.com <p> +www.panasonic.com<p> +www.kemet.com<p> +http://www.secc.co.jp/pdf/os_e/2004/e_os_all.pdf <b>(SANYO)</b> +<p> +for trimmer refence see : <u>www.electrospec-inc.com/cross_references/trimpotcrossref.asp</u><p> + +<table border=0 cellspacing=0 cellpadding=0 width="100%" cellpaddding=0> +<tr valign="top"> + +<! <td width="10">&nbsp;</td> +<td width="90%"> + +<b><font color="#0000FF" size="4">TRIM-POT CROSS REFERENCE</font></b> +<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=2> + <TR> + <TD COLSPAN=8> + <FONT SIZE=3 FACE=ARIAL><B>RECTANGULAR MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BOURNS</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BI&nbsp;TECH</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">DALE-VISHAY</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PHILIPS/MEPCO</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MURATA</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PANASONIC</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">SPECTROL</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MILSPEC</FONT> + </B> + </TD><TD>&nbsp;</TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3 > + 3005P<BR> + 3006P<BR> + 3006W<BR> + 3006Y<BR> + 3009P<BR> + 3009W<BR> + 3009Y<BR> + 3057J<BR> + 3057L<BR> + 3057P<BR> + 3057Y<BR> + 3059J<BR> + 3059L<BR> + 3059P<BR> + 3059Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 89P<BR> + 89W<BR> + 89X<BR> + 89PH<BR> + 76P<BR> + 89XH<BR> + 78SLT<BR> + 78L&nbsp;ALT<BR> + 56P&nbsp;ALT<BR> + 78P&nbsp;ALT<BR> + T8S<BR> + 78L<BR> + 56P<BR> + 78P<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + T18/784<BR> + 783<BR> + 781<BR> + -<BR> + -<BR> + -<BR> + 2199<BR> + 1697/1897<BR> + 1680/1880<BR> + 2187<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 8035EKP/CT20/RJ-20P<BR> + -<BR> + RJ-20X<BR> + -<BR> + -<BR> + -<BR> + 1211L<BR> + 8012EKQ&nbsp;ALT<BR> + 8012EKR&nbsp;ALT<BR> + 1211P<BR> + 8012EKJ<BR> + 8012EKL<BR> + 8012EKQ<BR> + 8012EKR<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 2101P<BR> + 2101W<BR> + 2101Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 2102L<BR> + 2102S<BR> + 2102Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVMCOG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 43P<BR> + 43W<BR> + 43Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 40L<BR> + 40P<BR> + 40Y<BR> + 70Y-T602<BR> + 70L<BR> + 70P<BR> + 70Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + RT/RTR12<BR> + RT/RTR12<BR> + RT/RTR12<BR> + -<BR> + RJ/RJR12<BR> + RJ/RJR12<BR> + RJ/RJR12<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SQUARE MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3250L<BR> + 3250P<BR> + 3250W<BR> + 3250X<BR> + 3252P<BR> + 3252W<BR> + 3252X<BR> + 3260P<BR> + 3260W<BR> + 3260X<BR> + 3262P<BR> + 3262W<BR> + 3262X<BR> + 3266P<BR> + 3266W<BR> + 3266X<BR> + 3290H<BR> + 3290P<BR> + 3290W<BR> + 3292P<BR> + 3292W<BR> + 3292X<BR> + 3296P<BR> + 3296W<BR> + 3296X<BR> + 3296Y<BR> + 3296Z<BR> + 3299P<BR> + 3299W<BR> + 3299X<BR> + 3299Y<BR> + 3299Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + -<BR> + 64W&nbsp;ALT<BR> + -<BR> + 64P&nbsp;ALT<BR> + 64W&nbsp;ALT<BR> + 64X&nbsp;ALT<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66P<BR> + 66W<BR> + 66X<BR> + 67P<BR> + 67W<BR> + 67X<BR> + 67Y<BR> + 67Z<BR> + 68P<BR> + 68W<BR> + 68X<BR> + 67Y&nbsp;ALT<BR> + 67Z&nbsp;ALT<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 5050<BR> + 5091<BR> + 5080<BR> + 5087<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + T63YB<BR> + T63XB<BR> + -<BR> + -<BR> + -<BR> + 5887<BR> + 5891<BR> + 5880<BR> + -<BR> + -<BR> + -<BR> + T93Z<BR> + T93YA<BR> + T93XA<BR> + T93YB<BR> + T93XB<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 8026EKP<BR> + 8026EKW<BR> + 8026EKM<BR> + 8026EKP<BR> + 8026EKB<BR> + 8026EKM<BR> + 1309X<BR> + 1309P<BR> + 1309W<BR> + 8024EKP<BR> + 8024EKW<BR> + 8024EKN<BR> + RJ-9P/CT9P<BR> + RJ-9W<BR> + RJ-9X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3105P/3106P<BR> + 3105W/3106W<BR> + 3105X/3106X<BR> + 3105Y/3106Y<BR> + 3105Z/3105Z<BR> + 3102P<BR> + 3102W<BR> + 3102X<BR> + 3102Y<BR> + 3102Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMCBG<BR> + EVMCCG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 55-1-X<BR> + 55-4-X<BR> + 55-3-X<BR> + 55-2-X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 50-2-X<BR> + 50-4-X<BR> + 50-3-X<BR> + -<BR> + -<BR> + -<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 64Y<BR> + 64Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3323P<BR> + 3323S<BR> + 3323W<BR> + 3329H<BR> + 3329P<BR> + 3329W<BR> + 3339H<BR> + 3339P<BR> + 3339W<BR> + 3352E<BR> + 3352H<BR> + 3352K<BR> + 3352P<BR> + 3352T<BR> + 3352V<BR> + 3352W<BR> + 3362H<BR> + 3362M<BR> + 3362P<BR> + 3362R<BR> + 3362S<BR> + 3362U<BR> + 3362W<BR> + 3362X<BR> + 3386B<BR> + 3386C<BR> + 3386F<BR> + 3386H<BR> + 3386K<BR> + 3386M<BR> + 3386P<BR> + 3386S<BR> + 3386W<BR> + 3386X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 25P<BR> + 25S<BR> + 25RX<BR> + 82P<BR> + 82M<BR> + 82PA<BR> + -<BR> + -<BR> + -<BR> + 91E<BR> + 91X<BR> + 91T<BR> + 91B<BR> + 91A<BR> + 91V<BR> + 91W<BR> + 25W<BR> + 25V<BR> + 25P<BR> + -<BR> + 25S<BR> + 25U<BR> + 25RX<BR> + 25X<BR> + 72XW<BR> + 72XL<BR> + 72PM<BR> + 72RX<BR> + -<BR> + 72PX<BR> + 72P<BR> + 72RXW<BR> + 72RXL<BR> + 72X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + T7YB<BR> + T7YA<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + TXD<BR> + TYA<BR> + TYP<BR> + -<BR> + TYD<BR> + TX<BR> + -<BR> + 150SX<BR> + 100SX<BR> + 102T<BR> + 101S<BR> + 190T<BR> + 150TX<BR> + 101<BR> + -<BR> + -<BR> + 101SX<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ET6P<BR> + ET6S<BR> + ET6X<BR> + RJ-6W/8014EMW<BR> + RJ-6P/8014EMP<BR> + RJ-6X/8014EMX<BR> + TM7W<BR> + TM7P<BR> + TM7X<BR> + -<BR> + 8017SMS<BR> + -<BR> + 8017SMB<BR> + 8017SMA<BR> + -<BR> + -<BR> + CT-6W<BR> + CT-6H<BR> + CT-6P<BR> + CT-6R<BR> + -<BR> + CT-6V<BR> + CT-6X<BR> + -<BR> + -<BR> + 8038EKV<BR> + -<BR> + 8038EKX<BR> + -<BR> + -<BR> + 8038EKP<BR> + 8038EKZ<BR> + 8038EKW<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 3321H<BR> + 3321P<BR> + 3321N<BR> + 1102H<BR> + 1102P<BR> + 1102T<BR> + RVA0911V304A<BR> + -<BR> + RVA0911H413A<BR> + RVG0707V100A<BR> + RVA0607V(H)306A<BR> + RVA1214H213A<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3104B<BR> + 3104C<BR> + 3104F<BR> + 3104H<BR> + -<BR> + 3104M<BR> + 3104P<BR> + 3104S<BR> + 3104W<BR> + 3104X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + EVMQ0G<BR> + EVMQIG<BR> + EVMQ3G<BR> + EVMS0G<BR> + EVMQ0G<BR> + EVMG0G<BR> + -<BR> + -<BR> + -<BR> + EVMK4GA00B<BR> + EVM30GA00B<BR> + EVMK0GA00B<BR> + EVM38GA00B<BR> + EVMB6<BR> + EVLQ0<BR> + -<BR> + EVMMSG<BR> + EVMMBG<BR> + EVMMAG<BR> + -<BR> + -<BR> + EVMMCS<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMM1<BR> + -<BR> + -<BR> + EVMM0<BR> + -<BR> + -<BR> + EVMM3<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 62-3-1<BR> + 62-1-2<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67R<BR> + -<BR> + 67P<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67X<BR> + 63V<BR> + 63S<BR> + 63M<BR> + -<BR> + -<BR> + 63H<BR> + 63P<BR> + -<BR> + -<BR> + 63X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P>&nbsp;<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=3> + <TR> + <TD COLSPAN=7> + <FONT color="#0000FF" SIZE=4 FACE=ARIAL><B>SMD TRIM-POT CROSS REFERENCE</B></FONT> + <P> + <FONT SIZE=4 FACE=ARIAL><B>MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3224G<BR> + 3224J<BR> + 3224W<BR> + 3269P<BR> + 3269W<BR> + 3269X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 44G<BR> + 44J<BR> + 44W<BR> + 84P<BR> + 84W<BR> + 84X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST63Z<BR> + ST63Y<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST5P<BR> + ST5W<BR> + ST5X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=7>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=7> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3314G<BR> + 3314J<BR> + 3364A/B<BR> + 3364C/D<BR> + 3364W/X<BR> + 3313G<BR> + 3313J<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 23B<BR> + 23A<BR> + 21X<BR> + 21W<BR> + -<BR> + 22B<BR> + 22A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST5YL/ST53YL<BR> + ST5YJ/5T53YJ<BR> + ST-23A<BR> + ST-22B<BR> + ST-22<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST-4B<BR> + ST-4A<BR> + -<BR> + -<BR> + -<BR> + ST-3B<BR> + ST-3A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVM-6YS<BR> + EVM-1E<BR> + EVM-1G<BR> + EVM-1D<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + G4B<BR> + G4A<BR> + TR04-3S1<BR> + TRG04-2S1<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + DVR-43A<BR> + CVR-42C<BR> + CVR-42A/C<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P> +<FONT SIZE=4 FACE=ARIAL><B>ALT =&nbsp;ALTERNATE</B></FONT> +<P> + +&nbsp; +<P> +</td> +</tr> +</table> + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 2.4 x 4.4 mm + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 2.5 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 3 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 4 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 5 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm, outline 6 x 5 mm + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 mm + 5 mm, outline 2.4 x 7 mm + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 2.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 3.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 4.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 2.5 + 5 mm, outline 5.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 2.4 x 4.4 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 2.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 4.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 3 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 5.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 7.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +Horizontal, grid 5 mm, outline 7.5 x 7.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 3.2 x 10.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 4.2 x 10.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 5.2 x 10.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm, outline 4.3 x 13.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm, outline 5.4 x 13.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm, outline 6.4 x 13.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 10.2 mm + 15.2 mm, outline 6.2 x 18.4 mm + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 5.4 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 6.4 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 7.2 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 8.4 x 18.3 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 15 mm, outline 9.1 x 18.2 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 6.2 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 7.4 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 8.7 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 10.8 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 22.5 mm, outline 11.3 x 26.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 9.3 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 11.3 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 13.4 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 20.5 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 32.5 mm, outline 13.7 x 37.4 mm + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 32.5 mm, outline 16.2 x 37.4 mm + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 32.5 mm, outline 18.2 x 37.4 mm + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 37.5 mm, outline 19.2 x 41.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 37.5 mm, outline 20.3 x 41.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 5 mm, outline 3.5 x 7.5 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 37.5 mm, outline 15.5 x 41.8 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 7.5 mm, outline 6.3 x 10.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 15.4 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>CAPACITOR</b><p> +grid 27.5 mm, outline 17.3 x 31.6 mm + + + + + + + + + + + + + + + + +>NAME +>VALUE + + +<b>Ceramic Chip Capacitor KEMET 0204 reflow solder</b><p> +Metric Code Size 1005 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 0603 reflow solder</b><p> +Metric Code Size 1608 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 0805 reflow solder</b><p> +Metric Code Size 2012 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1206 reflow solder</b><p> +Metric Code Size 3216 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1210 reflow solder</b><p> +Metric Code Size 3225 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1812 reflow solder</b><p> +Metric Code Size 4532 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 1825 reflow solder</b><p> +Metric Code Size 4564 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 2220 reflow solder</b><p>Metric Code Size 5650 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 2225 reflow solder</b><p>Metric Code Size 5664 + + + + +>NAME +>VALUE + + + + +<b> </b><p> +Source: http://www.vishay.com/docs/10129/hpc0201a.pdf + + +>NAME +>VALUE + + + +Source: http://www.avxcorp.com/docs/catalogs/cx5r.pdf + + +>NAME +>VALUE + + + + + + +<b>CAPACITOR</b><p> +Source: AVX .. aphvc.pdf + + + + +>NAME +>VALUE + + + + +<b>CAPACITOR</b><p> +Source: AVX .. aphvc.pdf + + + + +>NAME +>VALUE + + + + +<b>CAPACITOR</b> + + + +>NAME +>VALUE + + + + + + + + + + +>NAME +>VALUE + + + + + + + + +<B>CAPACITOR</B>, European symbol + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/ui.png b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/ui.png new file mode 100644 index 000000000..97c0ddc21 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rc2014_coleco/ui.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rgb_led/application.fam b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/application.fam new file mode 100644 index 000000000..2b7634b09 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/application.fam @@ -0,0 +1,16 @@ +App( + appid="RGB_LED", + name="[GPIO] RGB LED", + apptype=FlipperAppType.EXTERNAL, + entry_point="rgb_led_app", + stack_size=1 * 1024, + cdefines=["APP_RGB_LED"], + requires=[ + "gui", + ], + order=60, + fap_icon="rgb_led_10px.png", + fap_category="GPIO_Extra", + fap_icon_assets="assets", + +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/rgb_led/assets/Arr_dwn_7x9.png b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/assets/Arr_dwn_7x9.png new file mode 100644 index 000000000..d4034efc4 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/assets/Arr_dwn_7x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rgb_led/assets/Arr_up_7x9.png b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/assets/Arr_up_7x9.png new file mode 100644 index 000000000..28b4236a2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/assets/Arr_up_7x9.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rgb_led/led_ll.c b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/led_ll.c new file mode 100644 index 000000000..f84e9df2a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/led_ll.c @@ -0,0 +1,316 @@ +#include "led_ll.h" +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define TAG "LED_LL" + +#define LED_BPP 24 +#define ARR_TX 59 + +#define CCR_US_PERIOD 125 +#define CCR_US_0 40 +#define CCR_US_1 80 +#define CCR_0 (ARR_TX * CCR_US_0 / CCR_US_PERIOD) +#define CCR_1 (ARR_TX * CCR_US_1 / CCR_US_PERIOD) +#define CCR_RESET 0 +#define CCR_BUFFER_LEDS_HALF 8 +#define CCR_BUFFER_LEDS (CCR_BUFFER_LEDS_HALF * 2) +#define CCR_BUFFER_LEN (CCR_BUFFER_LEDS * LED_BPP) + + +static uint32_t rgb_buf[LED_COUNT]; +static uint32_t *rgb = rgb_buf; + +static uint32_t rgb_led_buf[LED_COUNT]; +static uint32_t *rgb_led = rgb_led_buf; + +static uint32_t rgb_offset = 0; +static uint8_t ccr_buffer[CCR_BUFFER_LEN]; + +static uint8_t brightness_i = 3; +static uint8_t is_transmitting = 0; + +static uint8_t apply_brightness(uint8_t v); +static uint8_t apply_gamma(uint8_t v); +static void fill_ccr_buffer(uint32_t ccr_led_offset, uint32_t ccr_led_count); + +const GpioPin gpio_led = {.port = GPIOA, .pin = LL_GPIO_PIN_4}; + +static void handle_dma() +{ + FURI_LOG_D("LED_LL", "HandleDMA Called"); + furi_crash("DMA ISR"); + + if(LL_DMA_IsActiveFlag_TE1(DMA1)) { + LL_DMA_ClearFlag_TE1(DMA1); + furi_crash("LED DMA Transfer did not work"); + } + else if (LL_DMA_IsActiveFlag_TC1(DMA1)) + { + if (rgb_offset > LED_COUNT) + { + LL_TIM_DisableCounter(TIM1); + is_transmitting = 0; + } + + fill_ccr_buffer(CCR_BUFFER_LEDS_HALF, CCR_BUFFER_LEDS_HALF); + rgb_offset += CCR_BUFFER_LEDS; + + LL_DMA_ClearFlag_TC1(DMA1); + } + else if (LL_DMA_IsActiveFlag_HT1(DMA1)) + { + fill_ccr_buffer(0, CCR_BUFFER_LEDS_HALF); + LL_DMA_ClearFlag_HT1(DMA1); + } +} + +void led_init() +{ + FURI_LOG_D(TAG, "Initializing"); + + furi_hal_gpio_init_ex(&gpio_led, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM1); + furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdDma1Ch1, 4, handle_dma, NULL); + + FURI_CRITICAL_ENTER(); + LL_TIM_DeInit(TIM1); + FURI_CRITICAL_EXIT(); + + LL_TIM_InitTypeDef tim_init = {0}; + LL_TIM_OC_InitTypeDef tim_oc_init = {0}; + + tim_init.Prescaler = 0; + tim_init.CounterMode = LL_TIM_COUNTERMODE_UP; + tim_init.Autoreload = ARR_TX; + tim_init.RepetitionCounter = 0; + + LL_TIM_Init(TIM1, &tim_init); + LL_TIM_EnableAllOutputs(TIM1); + LL_TIM_EnableDMAReq_CC3(TIM1); + LL_TIM_EnableIT_CC3(TIM1); + + tim_oc_init.OCMode = LL_TIM_OCMODE_PWM1; + tim_oc_init.OCState = LL_TIM_OCSTATE_ENABLE; + tim_oc_init.CompareValue = 0; + + LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &tim_oc_init); + LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1); + + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM1->CCR3); + dma_config.MemoryOrM2MDstAddress = (uint32_t)NULL; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = 0; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; + dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; //LL_DMA_PRIORITY_MEDIUM; // LL_DMA_PRIORITY_VERYHIGH + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); + LL_DMA_ClearFlag_TE1(DMA1); + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_HT1(DMA1); + LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1); +} + +void led_deinit() +{ + FURI_CRITICAL_ENTER(); + LL_TIM_DeInit(TIM1); + LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); + FURI_CRITICAL_EXIT(); +} + +int8_t led_update() +{ + FURI_LOG_D(TAG, "LED UPDATE"); + FURI_LOG_D(TAG, "LL_DMA_IsEnabledChannel = %lu", LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1)); + + if(is_transmitting) { + // ONLY FOR DEBUGGING RIGHT NOW + FURI_LOG_D(TAG, "Led Update but still transmitting"); + return -1; + } + /*while (is_transmitting) + { + }*/ + is_transmitting = 1; + + for (uint8_t i = 0; i < LED_COUNT; i++) + { + uint32_t v = rgb[i]; + uint8_t r = RGB_R(v); + uint8_t g = RGB_G(v); + uint8_t b = RGB_B(v); + + r = apply_gamma(r); + r = apply_brightness(r); + + g = apply_gamma(g); + g = apply_brightness(g); + + b = apply_gamma(b); + b = apply_brightness(b); + + rgb_led[i] = RGB_UINT(r, g, b); + } + + rgb_offset = 0; + fill_ccr_buffer(0, CCR_BUFFER_LEDS); + rgb_offset += CCR_BUFFER_LEDS; + + FURI_LOG_D(TAG, "Led Update with buffer %d %d:%d:%d:%d:%d:%d", CCR_BUFFER_LEN, + ccr_buffer[0], + ccr_buffer[1], + ccr_buffer[2], + ccr_buffer[3], + ccr_buffer[4], + ccr_buffer[5] + ); + + FURI_CRITICAL_ENTER(); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)ccr_buffer); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, CCR_BUFFER_LEN); + LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_TIM_GenerateEvent_UPDATE(TIM1); + LL_TIM_EnableCounter(TIM1); + FURI_CRITICAL_EXIT(); + + FURI_LOG_D(TAG, "Led Update. Timer is %lu", LL_TIM_CC_IsEnabledChannel(TIM1, LL_TIM_CHANNEL_CH1)); + + return 0; +} + +uint8_t led_get_brightness() { return brightness_i; } + +uint32_t led_get(uint16_t i) { return rgb[i]; } + +void led_set(uint16_t i, uint32_t v) { rgb[i] = v; } + +void led_set_rgb(uint16_t i, uint32_t r, uint32_t g, uint32_t b) +{ + if (i >= LED_COUNT) + { + return; + } + + if (r > 255) + r = 255; + if (g > 255) + g = 255; + if (b > 255) + b = 255; + + led_set(i, RGB_UINT(r, g, b)); +} + +void led_set_rgbf(uint16_t i, float r, float g, float b) +{ + if (r < 0) + r = 0; + if (g < 0) + g = 0; + if (b < 0) + b = 0; + + if (r > 1) + r = 1; + if (g > 1) + g = 1; + if (b > 1) + b = 1; + + led_set_rgb(i, r * 255, g * 255, b * 255); +} + +void led_clear() +{ + for (uint32_t i = 0; i < LED_COUNT; i++) + { + led_set(i, 0); + } +} + +void led_set_brightness(uint8_t brightness) +{ + if (brightness > LED_BRIGHTNESS_MAX - 1) + brightness = LED_BRIGHTNESS_MAX - 1; + + brightness_i = brightness; +} + +static uint8_t apply_brightness(uint8_t v) +{ + static uint8_t brightness_table[LED_BRIGHTNESS_MAX] = {50, 25, 12}; + + return (uint16_t)v * + brightness_table[(LED_BRIGHTNESS_MAX - 1) - brightness_i] / 255; +} + +static uint8_t apply_gamma(uint8_t v) +{ + static uint8_t gamma_table[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, + 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, + 10, 10, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, + 17, 17, 18, 18, 19, 19, 20, 21, 21, 22, 22, 23, 23, 24, 25, + 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 33, 34, 35, + 36, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 77, 78, + 79, 80, 81, 82, 84, 85, 86, 87, 88, 90, 91, 92, 93, 95, 96, + 97, 99, 100, 101, 103, 104, 105, 107, 108, 109, 111, 112, 114, 115, 117, + 118, 119, 121, 122, 124, 125, 127, 128, 130, 131, 133, 135, 136, 138, 139, + 141, 142, 144, 146, 147, 149, 151, 152, 154, 156, 157, 159, 161, 162, 164, + 166, 168, 169, 171, 173, 175, 176, 178, 180, 182, 184, 186, 187, 189, 191, + 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, + 223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 244, 246, 248, 250, 252, + 255}; + + return gamma_table[v]; +} + +static void fill_ccr_buffer(uint32_t ccr_led_offset, uint32_t ccr_led_count) +{ + for (uint32_t ccr_led_i = 0; ccr_led_i < ccr_led_count; ccr_led_i++) + { + uint8_t rgb_i = rgb_offset + ccr_led_offset + ccr_led_i; + uint32_t rgb_value = rgb_led[rgb_i]; + + uint16_t buff_offset = (ccr_led_offset + ccr_led_i) * LED_BPP; + for (uint8_t bit_i = 0; bit_i < 24; bit_i++) + { + uint16_t buff_i = buff_offset + bit_i; + + if (rgb_i >= LED_COUNT) + { + ccr_buffer[buff_i] = CCR_RESET; + } + else + { + uint8_t rgb_bit_offset = 23 - bit_i; + uint8_t rgb_bit = (rgb_value >> rgb_bit_offset) & 1; + + ccr_buffer[buff_i] = rgb_bit ? CCR_1 : CCR_0; + } + } + } +} + diff --git a/Applications/Official/DEV_FW/source/xMasterX/rgb_led/led_ll.h b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/led_ll.h new file mode 100644 index 000000000..c4a54211d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/led_ll.h @@ -0,0 +1,28 @@ +#ifndef _LED_H +#define _LED_H + +#include + +#define LED_BRIGHTNESS_MAX 3 +#define LED_COUNT 100 + +#define RGB_UINT(R, G, B) (((G) << 16) | ((R) << 8) | (B)) +#define RGB_R(V) ((V >> 8) & 0xFF) +#define RGB_G(V) ((V >> 16) & 0xFF) +#define RGB_B(V) (V & 0xFF) + + +void led_init(); +void led_deinit(); +int8_t led_update(); +uint8_t led_get_brightness(); +uint32_t led_get(uint16_t i); + +void led_set(uint16_t i, uint32_t v); +void led_set_rgb(uint16_t i, uint32_t r, uint32_t g, uint32_t b); +void led_set_rgbf(uint16_t i, float r, float g, float b); +void led_clear(); + +void led_set_brightness(uint8_t brightness); + +#endif \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/rgb_led/rgb_led.c b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/rgb_led.c new file mode 100644 index 000000000..2f0736426 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/rgb_led.c @@ -0,0 +1,118 @@ +#include "rgb_led.h" +#include +#include +#include +#define TAG "RgbLedApp" + +enum RgbLedSubmenuIndex { + RgbLedSubmenuIndexDefault, +}; + +void rgb_led_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + RgbLed* app = context; + if(index == RgbLedSubmenuIndexDefault) { + app->view_id = RgbLedViewDefault; + view_dispatcher_switch_to_view(app->view_dispatcher, RgbLedViewDefault); + } +} + +void rgb_led_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + RgbLed* app = context; + if(result == DialogExResultLeft) { + view_dispatcher_stop(app->view_dispatcher); + } else if(result == DialogExResultRight) { + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view + } else if(result == DialogExResultCenter) { + view_dispatcher_switch_to_view(app->view_dispatcher, RgbLedViewSubmenu); + } +} + +uint32_t rgb_led_exit_confirm_view(void* context) { + UNUSED(context); + return RgbLedViewExitConfirm; +} + +uint32_t rgb_led_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +RgbLed* rgb_led_app_alloc() { + RgbLed* app = malloc(sizeof(RgbLed)); + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // Notifications + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // View dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Submenu view + app->submenu = submenu_alloc(); + submenu_add_item( + app->submenu, "Default", RgbLedSubmenuIndexDefault, rgb_led_submenu_callback, app); + view_set_previous_callback(submenu_get_view(app->submenu), rgb_led_exit); + view_dispatcher_add_view(app->view_dispatcher, RgbLedViewSubmenu, submenu_get_view(app->submenu)); + + // Dialog view + app->dialog = dialog_ex_alloc(); + dialog_ex_set_result_callback(app->dialog, rgb_led_dialog_callback); + dialog_ex_set_context(app->dialog, app); + dialog_ex_set_left_button_text(app->dialog, "Exit"); + dialog_ex_set_right_button_text(app->dialog, "Stay"); + dialog_ex_set_center_button_text(app->dialog, "Menu"); + dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); + view_dispatcher_add_view( + app->view_dispatcher, RgbLedViewExitConfirm, dialog_ex_get_view(app->dialog)); + + // Default view + app->rgb_led_default = rgb_led_default_alloc(); + view_set_previous_callback( + rgb_led_default_get_view(app->rgb_led_default), rgb_led_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, RgbLedViewDefault, rgb_led_default_get_view(app->rgb_led_default)); + + // TODO switch to menu after Media is done + app->view_id = RgbLedViewSubmenu; + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + + return app; +} + +void rgb_led_app_free(RgbLed* app) { + furi_assert(app); + + // Reset notification + notification_internal_message(app->notifications, &sequence_reset_blue); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, RgbLedViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, RgbLedViewExitConfirm); + dialog_ex_free(app->dialog); + view_dispatcher_remove_view(app->view_dispatcher, RgbLedViewDefault); + rgb_led_default_free(app->rgb_led_default); + view_dispatcher_free(app->view_dispatcher); + // Close records + furi_record_close(RECORD_GUI); + app->gui = NULL; + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Free rest + free(app); +} + +int32_t rgb_led_app(void* p) { + UNUSED(p); + RgbLed* app = rgb_led_app_alloc(); + view_dispatcher_run(app->view_dispatcher); + rgb_led_app_free(app); + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/rgb_led/rgb_led.h b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/rgb_led.h new file mode 100644 index 000000000..e92b9f155 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/rgb_led.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include "views/rgb_led_default.h" + +typedef struct { + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + DialogEx* dialog; + RgbLedDefault* rgb_led_default; + uint32_t view_id; +} RgbLed; + +typedef enum { + RgbLedViewSubmenu, + RgbLedViewDefault, + RgbLedViewExitConfirm, +} RgbLedViewView; diff --git a/Applications/Official/DEV_FW/source/xMasterX/rgb_led/rgb_led_10px.png b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/rgb_led_10px.png new file mode 100644 index 000000000..713d79a8e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/rgb_led_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rgb_led/views/rgb_led_default.c b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/views/rgb_led_default.c new file mode 100644 index 000000000..a132b69c4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/views/rgb_led_default.c @@ -0,0 +1,127 @@ +#include "rgb_led_default.h" +#include +#include +#include +#include + +struct RgbLedDefault{ + View* view; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool back_pressed; + bool connected; +} RgbLedDefaultModel; + +uint8_t led_status = 0; + +void rgb_led_init() { + /// let's see it crashes + led_init(); +} + +#define COLORS_LEN 7 + +uint32_t colors[COLORS_LEN][3] = { + {255, 255, 255}, + {255, 0, 0}, + {0, 255, 0}, + {0, 0, 255}, + {255, 255, 0}, + {255, 0, 255}, + {0, 255, 255} +}; + +void rgb_led_fire() { + for (uint8_t i = 0; i < 15; i++) + { + uint8_t r = colors[3][0]; + uint8_t g = colors[3][1]; + uint8_t b = colors[3][2]; + + led_set_rgb(i, r, g, b); + } + + led_status = led_update(); + + +} + +static void rgb_led_default_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + RgbLedDefaultModel* model = context; + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 0, 20, AlignLeft, AlignTop, model->up_pressed ? "HIT" : "WAIT"); + elements_multiline_text_aligned(canvas, 0, 40, AlignLeft, AlignTop, led_status == 0 ? "LED OK" : "BUSY"); + return; +} + +static void rgb_led_default_process(RgbLedDefault* rgb_led_default, InputEvent* event) { + with_view_model( + rgb_led_default->view, + RgbLedDefaultModel * model, + { + if(event->type == InputTypePress) { + if(event->key == InputKeyUp) { + rgb_led_fire(); + model->up_pressed = true; + } + } else if(event->type == InputTypeRelease) { + if(event->key == InputKeyUp) { + model->up_pressed = false; + } + } + }, + true); +} + +static bool rgb_led_default_input_callback(InputEvent* event, void* context) { + furi_assert(context); + RgbLedDefault* rgb_led_default = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { +// furi_hal_hid_kb_release_all(); + } else { + rgb_led_default_process(rgb_led_default, event); + consumed = true; + } + + return consumed; +} + +RgbLedDefault* rgb_led_default_alloc() { + RgbLedDefault* rgb_led_default = malloc(sizeof(RgbLedDefault)); + rgb_led_default->view = view_alloc(); + view_set_context(rgb_led_default->view, rgb_led_default); + view_allocate_model(rgb_led_default->view, ViewModelTypeLocking, sizeof(RgbLedDefaultModel)); + view_set_draw_callback(rgb_led_default->view, rgb_led_default_draw_callback); + view_set_input_callback(rgb_led_default->view, rgb_led_default_input_callback); + + rgb_led_init(); + + return rgb_led_default; +} + +void rgb_led_default_free(RgbLedDefault* rgb_led_default) { + furi_assert(rgb_led_default); + view_free(rgb_led_default->view); + free(rgb_led_default); +} + +View* rgb_led_default_get_view(RgbLedDefault* rgb_led_default) { + furi_assert(rgb_led_default); + + return rgb_led_default->view; +} + +void rgb_led_default_set_connected_status(RgbLedDefault* rgb_led_default, bool connected) { + furi_assert(rgb_led_default); + with_view_model( + rgb_led_default->view, RgbLedDefaultModel * model, { model->connected = connected; }, true); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/rgb_led/views/rgb_led_default.h b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/views/rgb_led_default.h new file mode 100644 index 000000000..c66c9d670 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rgb_led/views/rgb_led_default.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include "../led_ll.h" + + +void rgb_led_init(void); + +typedef struct RgbLedDefault RgbLedDefault; + +RgbLedDefault* rgb_led_default_alloc(); + +void rgb_led_default_free(RgbLedDefault* rgb_led_default); + +View* rgb_led_default_get_view(RgbLedDefault* rgb_led_default); + +void rgb_led_default_set_connected_status(RgbLedDefault* rgb_led_default, bool connected); diff --git a/Applications/Official/DEV_FW/source/xMasterX/rmdice/application.fam b/Applications/Official/DEV_FW/source/xMasterX/rmdice/application.fam new file mode 100644 index 000000000..3ce862cc8 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rmdice/application.fam @@ -0,0 +1,12 @@ +App( + appid="RM_Dice", + name="RM Dice", + apptype=FlipperAppType.EXTERNAL, + entry_point="dice_app", + cdefines=["APP_DICE"], + requires=["gui"], + stack_size=2 * 1024, + order=70, + fap_icon="dice.png", + fap_category="Games_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/rmdice/dice.c b/Applications/Official/DEV_FW/source/xMasterX/rmdice/dice.c new file mode 100644 index 000000000..66d7edca2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rmdice/dice.c @@ -0,0 +1,575 @@ +#include +#include +#include "furi_hal_random.h" +#include +#include +#include + +#define TAG "Dice Roller" + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + FuriMutex* mutex; + FuriMessageQueue* event_queue; + //DesktopSettings* desktop_settings; + FuriHalRtcDateTime datetime; + uint8_t diceSelect; + uint8_t diceQty; + uint8_t diceRoll; + uint8_t playerOneScore; + uint8_t playerTwoScore; + char rollTime[1][15]; + char diceType[1][11]; + char strings[5][45]; + char theScores[1][45]; + bool letsRoll; +} DiceState; + +static void dice_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void dice_render_callback(Canvas* const canvas, void* ctx) { + DiceState* state = ctx; + if(furi_mutex_acquire(state->mutex, 200) != FuriStatusOk) { + // Can't obtain mutex, requeue render + PluginEvent event = {.type = EventTypeTick}; + furi_message_queue_put(state->event_queue, &event, 0); + return; + } + + canvas_set_font(canvas, FontSecondary); + if(state->diceSelect < 220) { + if(state->diceQty == 1) { + elements_button_left(canvas, "x1"); + } else if(state->diceQty == 2) { + elements_button_left(canvas, "x2"); + } else if(state->diceQty == 3) { + elements_button_left(canvas, "x3"); + } else if(state->diceQty == 4) { + elements_button_left(canvas, "x4"); + } else if(state->diceQty == 5) { + elements_button_left(canvas, "x5"); + } else if(state->diceQty == 6) { + elements_button_left(canvas, "x6"); + } + } + if(state->letsRoll) { + furi_hal_rtc_get_datetime(&state->datetime); + uint8_t hour = state->datetime.hour; + char strAMPM[3]; + snprintf(strAMPM, sizeof(strAMPM), "%s", "AM"); + if(hour > 12) { + hour -= 12; + snprintf(strAMPM, sizeof(strAMPM), "%s", "PM"); + } + snprintf( + state->rollTime[0], + sizeof(state->rollTime[0]), + "%.2d:%.2d:%.2d %s", + hour, + state->datetime.minute, + state->datetime.second, + strAMPM); + if(state->diceSelect == 229) { + const char* eightBall[] = { + "It is certain", + "Without a doubt", + "You may rely on it", + "Yes definitely", + "It is decidedly so", + "As I see it, yes", + "Most likely", + "Yes", + "Outlook good", + "Signs point to yes", + "Reply hazy try again", + "Better not tell you now", + "Ask again later", + "Cannot predict now", + "Concentrate and ask again", + "Don't count on it", + "Outlook not so good", + "My sources say no", + "Very doubtful", + "My reply is no"}; + state->diceRoll = + ((rand() % state->diceSelect) + 1); // JUST TO GET IT GOING? AND FIX BUG + snprintf(state->diceType[0], sizeof(state->diceType[0]), "%s", "8BALL"); + snprintf( + state->strings[0], + sizeof(state->strings[0]), + "%s at %s", + state->diceType[0], + state->rollTime[0]); + uint8_t d1_i = rand() % COUNT_OF(eightBall); + snprintf(state->strings[1], sizeof(state->strings[1]), "%s", eightBall[d1_i]); + } else if(state->diceSelect == 228) { + const char* eightBall[] = { + "I'd do it.", + "Hell, yeah!", + "You bet your life!", + "What are you waiting for?", + "You could do worse things.", + "Sure, I won't tell.", + "Yeah, you got this. Would I lie to you?", + "Looks like fun to me. ", + "Yeah, sure, why not?", + "DO IT!!!", + "Who's it gonna hurt?", + "Can you blame someone else?", + "Ask me again later.", + "Maybe, maybe not, I can't tell right now. ", + "Are you the betting type? ", + "Don't blame me if you get caught.", + "What have you got to lose?", + "I wouldn't if I were you.", + "My money's on the snowball.", + "Oh Hell no!"}; + state->diceRoll = + ((rand() % state->diceSelect) + 1); // JUST TO GET IT GOING? AND FIX BUG + snprintf(state->diceType[0], sizeof(state->diceType[0]), "%s", "Devil Ball"); + snprintf( + state->strings[0], + sizeof(state->strings[0]), + "%s at %s", + state->diceType[0], + state->rollTime[0]); + uint8_t d1_i = rand() % COUNT_OF(eightBall); + snprintf(state->strings[1], sizeof(state->strings[1]), "%s", eightBall[d1_i]); + } else if(state->diceSelect == 230) { + const char* diceOne[] = { + "Nibble", + "Massage", + "Touch", + "Caress", + "Pet", + "Fondle", + "Suck", + "Lick", + "Blow", + "Kiss", + "???"}; + const char* diceTwo[] = { + "Navel", + "Ears", + "Lips", + "Neck", + "Hand", + "Thigh", + "Nipple", + "Breasts", + "???", + "Genitals"}; + state->diceRoll = + ((rand() % state->diceSelect) + 1); // JUST TO GET IT GOING? AND FIX BUG + snprintf(state->diceType[0], sizeof(state->diceType[0]), "%s", "SEX?"); + snprintf( + state->strings[0], + sizeof(state->strings[0]), + "%s at %s", + state->diceType[0], + state->rollTime[0]); + uint8_t d1_i = rand() % COUNT_OF(diceOne); + uint8_t d2_i = rand() % COUNT_OF(diceTwo); + snprintf( + state->strings[1], + sizeof(state->strings[1]), + "%s %s", + diceOne[d1_i], + diceTwo[d2_i]); + } else if(state->diceSelect == 231) { + const char* deckOne[] = {"2H", "2C", "2D", "2S", "3H", "3C", "3D", "3S", "4H", + "4C", "4D", "4S", "5H", "5C", "5D", "5S", "6H", "6C", + "6D", "6S", "7H", "7C", "7D", "7S", "8H", "8C", "8D", + "8S", "9H", "9C", "9D", "9S", "10H", "10C", "10D", "10S", + "JH", "JC", "JD", "JS", "QH", "QC", "QD", "QS", "KH", + "KC", "KD", "KS", "AH", "AC", "AD", "AS"}; + char* deckTwo[] = {"2H", "2C", "2D", "2S", "3H", "3C", "3D", "3S", "4H", + "4C", "4D", "4S", "5H", "5C", "5D", "5S", "6H", "6C", + "6D", "6S", "7H", "7C", "7D", "7S", "8H", "8C", "8D", + "8S", "9H", "9C", "9D", "9S", "10H", "10C", "10D", "10S", + "JH", "JC", "JD", "JS", "QH", "QC", "QD", "QS", "KH", + "KC", "KD", "KS", "AH", "AC", "AD"}; // ONE LESS SINCE ONE WILL BE REMOVED + state->diceRoll = + ((rand() % state->diceSelect) + 1); // JUST TO GET IT GOING? AND FIX BUG + snprintf(state->diceType[0], sizeof(state->diceType[0]), "%s", "WAR!"); + snprintf( + state->strings[0], + sizeof(state->strings[0]), + "%s at %s", + state->diceType[0], + state->rollTime[0]); + uint8_t d1_i = rand() % COUNT_OF(deckOne); + // INITIALIZE WITH PLACEHOLDERS TO AVOID MAYBE UNINITIALIZED ERROR + for(uint8_t i = 0; i < COUNT_OF(deckOne); i++) { + if(i < d1_i) { + snprintf(deckTwo[i], 8, "%s", deckOne[i]); + } else if(i > d1_i) { + snprintf(deckTwo[i - 1], 8, "%s", deckOne[i]); + } + } + uint8_t d2_i = rand() % COUNT_OF(deckTwo); + if(d1_i > d2_i) { + state->playerOneScore++; + snprintf( + state->strings[1], + sizeof(state->strings[1]), + "%s > %s", + deckOne[d1_i], + deckTwo[d2_i]); + } else { + state->playerTwoScore++; + snprintf( + state->strings[1], + sizeof(state->strings[1]), + "%s < %s", + deckOne[d1_i], + deckTwo[d2_i]); + } + } else if(state->diceSelect == 232) { + const char* diceOne[] = { + "You", "You choose", "Nobody", "Everyone", "Nose goes", "Player to your right"}; + const char* diceTwo[] = { + "take a tiny toke", + "just chill", + "take 2 tokes", + "take a huge hit", + "bogart it", + "take a puff"}; + const char* diceThree[] = { + "while humming a tune", + "with your eyes closed", + "on your knees", + "while holding your nose", + "while spinning in a circle", + "in slow motion"}; + const char* diceFour[] = { + "twice", + "then tell a joke", + "then laugh as hard as you can", + "with the player to your left", + "then sing a song", + "then do a dance"}; + state->diceRoll = + ((rand() % state->diceSelect) + 1); // JUST TO GET IT GOING? AND FIX BUG + snprintf(state->diceType[0], sizeof(state->diceType[0]), "%s", "WEED!"); + snprintf( + state->strings[0], + sizeof(state->strings[0]), + "%s at %s", + state->diceType[0], + state->rollTime[0]); + uint8_t d1_i = rand() % COUNT_OF(diceOne); + uint8_t d2_i = rand() % COUNT_OF(diceTwo); + uint8_t d3_i = rand() % COUNT_OF(diceThree); + uint8_t d4_i = rand() % COUNT_OF(diceFour); + snprintf(state->strings[1], sizeof(state->strings[1]), "%s", diceOne[d1_i]); + snprintf(state->strings[2], sizeof(state->strings[2]), "%s", diceTwo[d2_i]); + snprintf(state->strings[3], sizeof(state->strings[3]), "%s", diceThree[d3_i]); + snprintf(state->strings[4], sizeof(state->strings[4]), "%s", diceFour[d4_i]); + } else { + state->diceRoll = ((rand() % state->diceSelect) + 1); + snprintf( + state->diceType[0], sizeof(state->diceType[0]), "%s%d", "d", state->diceSelect); + snprintf( + state->strings[0], + sizeof(state->strings[0]), + "%d%s at %s", + state->diceQty, + state->diceType[0], + state->rollTime[0]); + /*if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect) + DOLPHIN_DEED(getRandomDeed()); + if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect - 1) + DOLPHIN_DEED(getRandomDeed());*/ + if(state->diceQty == 1) { + snprintf(state->strings[1], sizeof(state->strings[1]), "%d", state->diceRoll); + } else if(state->diceQty == 2) { + snprintf( + state->strings[1], + sizeof(state->strings[1]), + "%d %d", + state->diceRoll, + ((rand() % state->diceSelect) + 1)); + } else if(state->diceQty == 3) { + snprintf( + state->strings[1], + sizeof(state->strings[1]), + "%d %d %d", + state->diceRoll, + ((rand() % state->diceSelect) + 1), + ((rand() % state->diceSelect) + 1)); + } else if(state->diceQty == 4) { + snprintf( + state->strings[1], + sizeof(state->strings[1]), + "%d %d %d %d", + state->diceRoll, + ((rand() % state->diceSelect) + 1), + ((rand() % state->diceSelect) + 1), + ((rand() % state->diceSelect) + 1)); + } else if(state->diceQty == 5) { + snprintf( + state->strings[1], + sizeof(state->strings[1]), + "%d %d %d %d %d", + state->diceRoll, + ((rand() % state->diceSelect) + 1), + ((rand() % state->diceSelect) + 1), + ((rand() % state->diceSelect) + 1), + ((rand() % state->diceSelect) + 1)); + } else if(state->diceQty == 6) { + snprintf( + state->strings[1], + sizeof(state->strings[1]), + "%d %d %d %d %d %d", + state->diceRoll, + ((rand() % state->diceSelect) + 1), + ((rand() % state->diceSelect) + 1), + ((rand() % state->diceSelect) + 1), + ((rand() % state->diceSelect) + 1), + ((rand() % state->diceSelect) + 1)); + } + } + state->letsRoll = false; + } + furi_mutex_release(state->mutex); + if(state->diceRoll != 0) { + if(state->diceSelect == 232) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, state->strings[0]); + canvas_draw_str_aligned(canvas, 64, 18, AlignCenter, AlignCenter, state->strings[1]); + canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignCenter, state->strings[2]); + canvas_draw_str_aligned(canvas, 64, 34, AlignCenter, AlignCenter, state->strings[3]); + canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignCenter, state->strings[4]); + } else if(state->diceSelect == 228 || state->diceSelect == 229) { + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignCenter, state->strings[1]); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, state->strings[0]); + } else { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignCenter, state->strings[1]); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, state->strings[0]); + } + if(state->diceSelect == 231 && + !(state->playerOneScore == 0 && state->playerTwoScore == 0)) { + canvas_set_font(canvas, FontSecondary); + snprintf( + state->theScores[0], + sizeof(state->theScores[0]), + "%d %d", + state->playerOneScore, + state->playerTwoScore); + canvas_draw_str_aligned(canvas, 64, 34, AlignCenter, AlignCenter, state->theScores[0]); + } + } + if(state->diceSelect == 229 || state->diceSelect == 228) { + elements_button_center(canvas, "Shake"); + } else if(state->diceSelect == 231) { + elements_button_center(canvas, "Draw"); + } else { + elements_button_center(canvas, "Roll"); + } + if(state->diceSelect == 2) { + elements_button_right(canvas, "d2"); + } else if(state->diceSelect == 3) { + elements_button_right(canvas, "d3"); + } else if(state->diceSelect == 4) { + elements_button_right(canvas, "d4"); + } else if(state->diceSelect == 6) { + elements_button_right(canvas, "d6"); + } else if(state->diceSelect == 8) { + elements_button_right(canvas, "d8"); + } else if(state->diceSelect == 10) { + elements_button_right(canvas, "d10"); + } else if(state->diceSelect == 12) { + elements_button_right(canvas, "d12"); + } else if(state->diceSelect == 20) { + elements_button_right(canvas, "d20"); + } else if(state->diceSelect == 59) { + elements_button_right(canvas, "d59"); + } else if(state->diceSelect == 69) { + elements_button_right(canvas, "d69"); + } else if(state->diceSelect == 100) { + elements_button_right(canvas, "d100"); + } else if(state->diceSelect == 229) { + elements_button_right(canvas, "8BALL"); + } else if(state->diceSelect == 228) { + elements_button_right(canvas, "DBALL"); + } else if(state->diceSelect == 230) { + elements_button_right(canvas, "SEX"); + } else if(state->diceSelect == 231) { + elements_button_right(canvas, "WAR"); + } else if(state->diceSelect == 232) { + elements_button_right(canvas, "WEED"); + } +} + +static void dice_state_init(DiceState* const state) { + memset(state, 0, sizeof(DiceState)); + furi_hal_rtc_get_datetime(&state->datetime); + state->diceSelect = 20; + state->diceQty = 1; + state->diceRoll = 0; + state->playerOneScore = 0; + state->playerTwoScore = 0; + state->letsRoll = false; + //state->desktop_settings = malloc(sizeof(DesktopSettings)); +} + +static void dice_tick(void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + PluginEvent event = {.type = EventTypeTick}; + // It's OK to lose this event if system overloaded + furi_message_queue_put(event_queue, &event, 0); +} + +int32_t dice_app(void* p) { + UNUSED(p); + DiceState* plugin_state = malloc(sizeof(DiceState)); + dice_state_init(plugin_state); + plugin_state->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + if(plugin_state->event_queue == NULL) { + FURI_LOG_E(TAG, "cannot create event queue\n"); + free(plugin_state); + return 255; + } + + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(plugin_state->mutex == NULL) { + FURI_LOG_E(TAG, "cannot create mutex\n"); + furi_message_queue_free(plugin_state->event_queue); + free(plugin_state); + return 255; + } + + FuriTimer* timer = + furi_timer_alloc(dice_tick, FuriTimerTypePeriodic, plugin_state->event_queue); + if(timer == NULL) { + FURI_LOG_E(TAG, "cannot create timer\n"); + furi_mutex_free(plugin_state->mutex); + furi_message_queue_free(plugin_state->event_queue); + free(plugin_state); + return 255; + } + + //DESKTOP_SETTINGS_LOAD(plugin_state->desktop_settings); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, dice_render_callback, plugin_state); + view_port_input_callback_set(view_port, dice_input_callback, plugin_state->event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + furi_timer_start(timer, furi_kernel_get_tick_frequency()); + + // Main loop + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(plugin_state->event_queue, &event, 100); + if(event_status == FuriStatusOk) { + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + if(plugin_state->diceSelect == 2) { + plugin_state->diceSelect = 3; + } else if(plugin_state->diceSelect == 3) { + plugin_state->diceSelect = 4; + } else if(plugin_state->diceSelect == 4) { + plugin_state->diceSelect = 6; + } else if(plugin_state->diceSelect == 6) { + plugin_state->diceSelect = 8; + } else if(plugin_state->diceSelect == 8) { + plugin_state->diceSelect = 10; + } else if(plugin_state->diceSelect == 10) { + plugin_state->diceSelect = 12; + } else if(plugin_state->diceSelect == 12) { + plugin_state->diceSelect = 20; + } else if(plugin_state->diceSelect == 20) { + plugin_state->diceSelect = 100; + } else if(plugin_state->diceSelect == 100) { + /*if(plugin_state->desktop_settings->is_dumbmode) { + plugin_state->diceSelect = 231; + } else {*/ + plugin_state->diceSelect = 230; + //} + } else if(plugin_state->diceSelect == 230) { + plugin_state->playerOneScore = 0; + plugin_state->playerTwoScore = 0; + plugin_state->diceSelect = 231; + } else if(plugin_state->diceSelect == 231) { + plugin_state->diceSelect = 229; + } else if(plugin_state->diceSelect == 229) { + plugin_state->diceSelect = 228; + } else if(plugin_state->diceSelect == 228) { + /*if(plugin_state->desktop_settings->is_dumbmode) { + plugin_state->diceSelect = 59; + } else {*/ + plugin_state->diceSelect = 232; + //} + } else if(plugin_state->diceSelect == 232) { + plugin_state->diceSelect = 59; + } else if(plugin_state->diceSelect == 59) { + plugin_state->diceSelect = 69; + } else { + plugin_state->diceSelect = 2; + } + break; + case InputKeyLeft: + if(plugin_state->diceQty <= 5) { + plugin_state->diceQty = plugin_state->diceQty + 1; + } else { + plugin_state->diceQty = 1; + } + break; + case InputKeyOk: + plugin_state->letsRoll = true; + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + // furi_hal_rtc_get_datetime(&plugin_state->datetime); + } + view_port_update(view_port); + furi_mutex_release(plugin_state->mutex); + } else { + // FURI_LOG_D(TAG, "osMessageQueue: event timeout"); + } + } + // Cleanup + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(plugin_state->event_queue); + furi_mutex_free(plugin_state->mutex); + //free(plugin_state->desktop_settings); + free(plugin_state); + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/rmdice/dice.png b/Applications/Official/DEV_FW/source/xMasterX/rmdice/dice.png new file mode 100644 index 000000000..64928b1de Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rmdice/dice.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/LICENSE new file mode 100644 index 000000000..4a6de25cb --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 RaZe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/README.md b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/README.md new file mode 100644 index 000000000..feddf06d6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/README.md @@ -0,0 +1,13 @@ +# Rubik's Cube Scrambler FAP + +## Where to start? +Install the .fap file and put it in your apps folder + +## What does what? +The On/Off button toggles the vibration notification on and off. The "New" button generates a new scramble. The scramble letters correspond with the following moves: R = Right, L = Left, U = Up, D = Down, F = Front, B = Back. The number after the letter indicates how many times to turn that face. For example, R2 means to turn the right face twice. The ' symbol indicates a counter-clockwise turn. For example, R' means to turn the right face counter-clockwise once. + + + +# A special thanks to Tanish for their c scrambler example 🙏 +https://github.com/TanishBhongade/RubiksCubeScrambler-C/ + diff --git a/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/application.fam b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/application.fam new file mode 100644 index 000000000..56da69378 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/application.fam @@ -0,0 +1,20 @@ +# COMPILE ISTRUCTIONS: + +# Clean the code and remove old binaries/compilation artefact +# ./fbt -c fap_rubiks_cube_scrambler + +# Compile FAP +# ./fbt fap_rubiks_cube_scrambler + +# Run application directly inside the Flip.x0 +# ./fbt launch_app APPSRC=rubiks_cube_scrambler + +App( + appid="rubiks_cube_scrambler", + name="Rubik's Cube Scrambler", + apptype=FlipperAppType.EXTERNAL, + entry_point="rubiks_cube_scrambler_main", + stack_size=1 * 1024, + fap_category="Misc_Extra", + fap_icon="cube.png", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/cube.png b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/cube.png new file mode 100644 index 000000000..3178a29d6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/cube.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/rubiks_cube_scrambler.c b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/rubiks_cube_scrambler.c new file mode 100644 index 000000000..8857c6b80 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/rubiks_cube_scrambler.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include + +#include "scrambler.h" +#include "furi_hal_random.h" + +int scrambleStarted = 0; +char scramble_str[100] = {0}; +char scramble_start[100] = {0}; +char scramble_end[100] = {0}; +int notifications_enabled = 0; + +static void success_vibration() +{ + furi_hal_vibro_on(false); + furi_hal_vibro_on(true); + furi_delay_ms(50); + furi_hal_vibro_on(false); + return; +} +void split_array(char original[], int size, char first[], char second[]) { + int mid = size / 2; + if (size % 2 != 0) { + mid++; + } + int first_index = 0, second_index = 0; + for (int i = 0; i < size; i++) { + if (i < mid) { + first[first_index++] = original[i]; + } else { + if (i == mid && (original[i] == '2' || original[i] == '\'')) { + continue; + } + second[second_index++] = original[i]; + } + } + first[first_index] = '\0'; + second[second_index] = '\0'; +} + + +static void draw_callback(Canvas *canvas, void *ctx) +{ + UNUSED(ctx); + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 13, "Rubik's Cube Scrambler"); + + if (scrambleStarted) + { + genScramble(); + scrambleReplace(); + strcpy(scramble_str, printData()); + if (notifications_enabled) + { + success_vibration(); + } + split_array(scramble_str, strlen(scramble_str), scramble_start, scramble_end); + scrambleStarted = 0; + } + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, scramble_start); + canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignCenter, scramble_end); + elements_button_center(canvas, "New"); + + elements_button_left(canvas, notifications_enabled ? "On" : "Off"); +}; + +static void input_callback(InputEvent *input_event, void *ctx) +{ + furi_assert(ctx); + FuriMessageQueue *event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t rubiks_cube_scrambler_main(void *p) +{ + UNUSED(p); + InputEvent event; + + FuriMessageQueue *event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + ViewPort *view_port = view_port_alloc(); + + view_port_draw_callback_set(view_port, draw_callback, NULL); + + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui *gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + while (true) + { + furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); + + if (event.key == InputKeyOk && event.type == InputTypeShort) + { + scrambleStarted = 1; + } + if (event.key == InputKeyLeft && event.type == InputTypeShort) + { + if (notifications_enabled) + { + notifications_enabled = 0; + } + else + { + notifications_enabled = 1; + success_vibration(); + } + } + if (event.key == InputKeyBack) + { + break; + } + } + + furi_message_queue_free(event_queue); + + gui_remove_view_port(gui, view_port); + + view_port_free(view_port); + furi_record_close(RECORD_GUI); + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/scrambler.c b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/scrambler.c new file mode 100644 index 000000000..98ebaed40 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/scrambler.c @@ -0,0 +1,118 @@ +/* +Authors: Tanish Bhongade and RaZe +*/ + +#include +#include +#include +#include "furi_hal_random.h" +#include +#include +#include "scrambler.h" + +// 6 moves along with direction +char moves[6] = {'R', 'U', 'F', 'B', 'L', 'D'}; +char dir[4] = {' ', '\'', '2'}; +const int SLEN = 20; +#define RESULT_SIZE 100 +// Structure which holds main scramble +struct GetScramble +{ + char mainScramble[25][3]; +}; +struct GetScramble a; // Its object + +// Function prototypes to avoid bugs +void scrambleReplace(); +void genScramble(); +void valid(); +int getRand(int upr, int lwr); +char *printData(); +void writeToFile(); + +// Main function +/* int main(){ + genScramble ();//Calling genScramble + scrambleReplace();//Calling scrambleReplace + valid();//Calling valid to validate the scramble + printData ();//Printing the final scramble + //writeToFile();//If you want to write to a file, please uncomment this + + return 0; +} */ + +void genScramble() +{ + // Stage 1 + for (int i = 0; i < SLEN; i++) + { + strcpy(a.mainScramble[i], "00"); + } + // This makes array like this 00 00 00....... +} + +void scrambleReplace() +{ + // Stage 2 + // Actual process begins here + + // Initialize the mainScramble array with all the possible moves + for (int i = 0; i < SLEN; i++) + { + a.mainScramble[i][0] = moves[furi_hal_random_get() % 6]; + a.mainScramble[i][1] = dir[furi_hal_random_get() % 3]; + } + + // Perform the Fisher-Yates shuffle + for (int i = 6 - 1; i > 0; i--) + { + int j = rand() % (i + 1); + char temp[3]; + strcpy(temp, a.mainScramble[i]); + strcpy(a.mainScramble[i], a.mainScramble[j]); + strcpy(a.mainScramble[j], temp); + } + + // Select the first 10 elements as the scramble, using only the first three elements of the dir array + for (int i = 0; i < SLEN; i++) + { + a.mainScramble[i][1] = dir[furi_hal_random_get() % 3]; + } + for (int i = 1; i < SLEN; i++) { + while ( a.mainScramble[i][0] == a.mainScramble[i - 2][0] || a.mainScramble[i][0] == a.mainScramble[i - 1][0]) { + a.mainScramble[i][0] = moves[furi_hal_random_get()%5]; + } + } +} + + + + + + + +// Let this function be here for now till I find out what is causing the extra space bug in the scrambles +void remove_double_spaces(char *str) { + int i, j; + int len = strlen(str); + for (i = 0, j = 0; i < len; i++, j++) { + if (str[i] == ' ' && str[i + 1] == ' ') { + i++; + } + str[j] = str[i]; + } + str[j] = '\0'; +} +char *printData() +{ + static char result[RESULT_SIZE]; + int offset = 0; + for (int loop = 0; loop < SLEN; loop++) + { + offset += snprintf(result + offset, RESULT_SIZE - offset, "%s ", a.mainScramble[loop]); + } + remove_double_spaces(result); + return result; +} + + diff --git a/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/scrambler.h b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/scrambler.h new file mode 100644 index 000000000..66f6b221d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/rubiks_cube_scrambler/scrambler.h @@ -0,0 +1,3 @@ +void scrambleReplace (); +void genScramble (); +char *printData (); diff --git a/Applications/Official/DEV_FW/source/xMasterX/sam/application.fam b/Applications/Official/DEV_FW/source/xMasterX/sam/application.fam new file mode 100644 index 000000000..c9fc7943d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/sam/application.fam @@ -0,0 +1,45 @@ +App( + appid="SAM", + name="SAM WTF", + apptype=FlipperAppType.PLUGIN, + entry_point="sam_app", + requires=[ + "gui", + "dialogs", + ], + stack_size=4 * 1024, + order=20, + fap_icon="icons/music_10px.png", + fap_category="Music_Extra", + fap_icon_assets="icons", +) +App( + appid="SAM_YES", + name="SAM YES", + apptype=FlipperAppType.PLUGIN, + entry_point="sam_app_yes", + requires=[ + "gui", + "dialogs", + ], + stack_size=4 * 1024, + order=20, + fap_icon="music_10px.png", + fap_category="Music_Extra", + fap_icon_assets="icons", +) +App( + appid="SAM_NO", + name="SAM NO", + apptype=FlipperAppType.PLUGIN, + entry_point="sam_app_no", + requires=[ + "gui", + "dialogs", + ], + stack_size=4 * 1024, + order=20, + fap_icon="music_10px.png", + fap_category="Music_Extra", + fap_icon_assets="icons", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/sam/icons/music_10px.png b/Applications/Official/DEV_FW/source/xMasterX/sam/icons/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/sam/icons/music_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/sam/music_10px.png b/Applications/Official/DEV_FW/source/xMasterX/sam/music_10px.png new file mode 100644 index 000000000..d41eb0db8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/sam/music_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/sam/sam_app.cpp b/Applications/Official/DEV_FW/source/xMasterX/sam/sam_app.cpp new file mode 100644 index 000000000..d9b319475 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/sam/sam_app.cpp @@ -0,0 +1,36 @@ +#include +#include +#include "stm32_sam.h" +// WOULD BE COOL IF SOMEONE MADE A TEXT ENTRY SCREEN TO HAVE IT READ WHAT IS ENTERED TO TEXT +STM32SAM voice; + +extern "C" int32_t sam_app(void* p) { + UNUSED(p); + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + voice.begin(); + voice.say( + "All your base are belong to us. You have no chance to survive make your time. ha. ha. ha. GOOD BYE. "); + furi_hal_speaker_release(); + } + return 0; +} + +extern "C" int32_t sam_app_yes(void* p) { + UNUSED(p); + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + voice.begin(); + voice.say("Yes"); + furi_hal_speaker_release(); + } + return 0; +} + +extern "C" int32_t sam_app_no(void* p) { + UNUSED(p); + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + voice.begin(); + voice.say("No"); + furi_hal_speaker_release(); + } + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/sam/stm32_sam.cpp b/Applications/Official/DEV_FW/source/xMasterX/sam/stm32_sam.cpp new file mode 100644 index 000000000..16f6fcaab --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/sam/stm32_sam.cpp @@ -0,0 +1,5704 @@ + +#include "stm32_sam.h" +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// All +// +//////////////////////////////////////////////////////////////////////////////////////////// + +char input[256 + 1] = {0}; //tab39445 +//standard sam sound + +unsigned char wait1 = 7; +unsigned char wait2 = 6; + +unsigned char A, X, Y; +unsigned char mem44; +unsigned char mem47; +unsigned char mem49; +unsigned char mem39; +unsigned char mem50; +unsigned char mem51; +unsigned char mem53; +unsigned char mem56; +unsigned char mem59 = 0; + +unsigned char phonemeIndexOutput[60]; //tab47296 +unsigned char stressOutput[60]; //tab47365 +unsigned char phonemeLengthOutput[60]; //tab47416 + +// contains the soundbuffer position +int bufferpos; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Sam Tabs +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//tab40672 +const unsigned char stressInputTable[] = {'*', '1', '2', '3', '4', '5', '6', '7', '8'}; + +//tab40682 +const unsigned char signInputTable1[] = { + ' ', '.', '?', ',', '-', 'I', 'I', 'E', 'A', 'A', 'A', 'A', 'U', 'A', 'I', 'E', 'U', + 'O', 'R', 'L', 'W', 'Y', 'W', 'R', 'L', 'W', 'Y', 'M', 'N', 'N', 'D', 'Q', 'S', 'S', + 'F', 'T', '/', '/', 'Z', 'Z', 'V', 'D', 'C', '*', 'J', '*', '*', '*', 'E', 'A', 'O', + 'A', 'O', 'U', 'B', '*', '*', 'D', '*', '*', 'G', '*', '*', 'G', '*', '*', 'P', '*', + '*', 'T', '*', '*', 'K', '*', '*', 'K', '*', '*', 'U', 'U', 'U'}; + +//tab40763 +const unsigned char signInputTable2[] = { + '*', '*', '*', '*', '*', 'Y', 'H', 'H', 'E', 'A', 'H', 'O', 'H', 'X', 'X', 'R', 'X', + 'H', 'X', 'X', 'X', 'X', 'H', '*', '*', '*', '*', '*', '*', 'X', 'X', '*', '*', 'H', + '*', 'H', 'H', 'X', '*', 'H', '*', 'H', 'H', '*', '*', '*', '*', '*', 'Y', 'Y', 'Y', + 'W', 'W', 'W', '*', '*', '*', '*', '*', '*', '*', '*', '*', 'X', '*', '*', '*', '*', + '*', '*', '*', '*', '*', '*', '*', 'X', '*', '*', 'L', 'M', 'N'}; + +//loc_9F8C +const unsigned char flags[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0x84, 0x84, 0xA4, + 0xA4, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4C, + 0x4C, 0x4C, 0x48, 0x4C, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x44, 0x44, 0x44, 0x44, + 0x48, 0x40, 0x4C, 0x44, 0x00, 0x00, 0xB4, 0xB4, 0xB4, 0x94, 0x94, 0x94, 0x4E, 0x4E, + 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4B, 0x4B, 0x4B, 0x4B, + 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x80, 0xC1, 0xC1 + +}; + +//??? flags overlap flags2 +//loc_9FDA +const unsigned char flags2[] = { + 0x80, 0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x08, 0x0C, 0x08, 0x04, 0x40, + 0x24, 0x20, 0x20, 0x24, 0x00, 0x00, 0x24, 0x20, 0x20, 0x24, 0x20, 0x20, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +//tab45616??? +const unsigned char phonemeStressedLengthTable[] = { + 0x00, 0x12, 0x12, 0x12, 8, 0xB, 9, 0xB, 0xE, 0xF, 0xB, 0x10, 0xC, 6, 6, 0xE, + 0xC, 0xE, 0xC, 0xB, 8, 8, 0xB, 0xA, 9, 8, 8, 8, 8, 8, 3, 5, + 2, 2, 2, 2, 2, 2, 6, 6, 8, 6, 6, 2, 9, 4, 2, 1, + 0xE, 0xF, 0xF, 0xF, 0xE, 0xE, 8, 2, 2, 7, 2, 1, 7, 2, 2, 7, + 2, 2, 8, 2, 2, 6, 2, 2, 7, 2, 4, 7, 1, 4, 5, 5}; + +//tab45536??? +const unsigned char phonemeLengthTable[] = { + 0, 0x12, 0x12, 0x12, 8, 8, 8, 8, 8, 0xB, 6, 0xC, 0xA, 5, 5, 0xB, 0xA, 0xA, 0xA, 9, + 8, 7, 9, 7, 6, 8, 6, 7, 7, 7, 2, 5, 2, 2, 2, 2, 2, 2, 6, 6, + 7, 6, 6, 2, 8, 3, 1, 0x1E, 0xD, 0xC, 0xC, 0xC, 0xE, 9, 6, 1, 2, 5, 1, 1, + 6, 1, 2, 6, 1, 2, 8, 2, 2, 4, 2, 2, 6, 1, 4, 6, 1, 4, 0xC7, 0xFF}; + +/* + + Ind | phoneme | flags | + -----|---------|----------| + 0 | * | 00000000 | + 1 | .* | 00000000 | + 2 | ?* | 00000000 | + 3 | ,* | 00000000 | + 4 | -* | 00000000 | + + VOWELS + 5 | IY | 10100100 | + 6 | IH | 10100100 | + 7 | EH | 10100100 | + 8 | AE | 10100100 | + 9 | AA | 10100100 | + 10 | AH | 10100100 | + 11 | AO | 10000100 | + 17 | OH | 10000100 | + 12 | UH | 10000100 | + 16 | UX | 10000100 | + 15 | ER | 10000100 | + 13 | AX | 10100100 | + 14 | IX | 10100100 | + + DIPHTONGS + 48 | EY | 10110100 | + 49 | AY | 10110100 | + 50 | OY | 10110100 | + 51 | AW | 10010100 | + 52 | OW | 10010100 | + 53 | UW | 10010100 | + + + 21 | YX | 10000100 | + 20 | WX | 10000100 | + 18 | RX | 10000100 | + 19 | LX | 10000100 | + 37 | /X | 01000000 | + 30 | DX | 01001000 | + + + 22 | WH | 01000100 | + + + VOICED CONSONANTS + 23 | R* | 01000100 | + 24 | L* | 01000100 | + 25 | W* | 01000100 | + 26 | Y* | 01000100 | + 27 | M* | 01001100 | + 28 | N* | 01001100 | + 29 | NX | 01001100 | + 54 | B* | 01001110 | + 57 | D* | 01001110 | + 60 | G* | 01001110 | + 44 | J* | 01001100 | + 38 | Z* | 01000100 | + 39 | ZH | 01000100 | + 40 | V* | 01000100 | + 41 | DH | 01000100 | + + unvoiced CONSONANTS + 32 | S* | 01000000 | + 33 | SH | 01000000 | + 34 | F* | 01000000 | + 35 | TH | 01000000 | + 66 | P* | 01001011 | + 69 | T* | 01001011 | + 72 | K* | 01001011 | + 42 | CH | 01001000 | + 36 | /H | 01000000 | + + 43 | ** | 01000000 | + 45 | ** | 01000100 | + 46 | ** | 00000000 | + 47 | ** | 00000000 | + + + 55 | ** | 01001110 | + 56 | ** | 01001110 | + 58 | ** | 01001110 | + 59 | ** | 01001110 | + 61 | ** | 01001110 | + 62 | ** | 01001110 | + 63 | GX | 01001110 | + 64 | ** | 01001110 | + 65 | ** | 01001110 | + 67 | ** | 01001011 | + 68 | ** | 01001011 | + 70 | ** | 01001011 | + 71 | ** | 01001011 | + 73 | ** | 01001011 | + 74 | ** | 01001011 | + 75 | KX | 01001011 | + 76 | ** | 01001011 | + 77 | ** | 01001011 | + + + SPECIAL + 78 | UL | 10000000 | + 79 | UM | 11000001 | + 80 | UN | 11000001 | + 31 | Q* | 01001100 | + +*/ + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// RenderTabs +// +//////////////////////////////////////////////////////////////////////////////////////////// + +const unsigned char tab48426[5] = {0x18, 0x1A, 0x17, 0x17, 0x17}; + +const unsigned char tab47492[] = {0, 0, 0xE0, 0xE6, 0xEC, 0xF3, 0xF9, 0, 6, 0xC, 6}; + +const unsigned char amplitudeRescale[] = { + 0, + 1, + 2, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 6, + 8, + 9, + 0xB, + 0xD, + 0xF, + 0 //17 elements? +}; + +// Used to decide which phoneme's blend lengths. The candidate with the lower score is selected. +// tab45856 +const unsigned char blendRank[] = {0, 0x1F, 0x1F, 0x1F, 0x1F, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 5, 5, 2, 0xA, 2, 8, + 5, 5, 0xB, 0xA, 9, 8, 8, 0xA0, 8, 8, + 0x17, 0x1F, 0x12, 0x12, 0x12, 0x12, 0x1E, 0x1E, 0x14, 0x14, + 0x14, 0x14, 0x17, 0x17, 0x1A, 0x1A, 0x1D, 0x1D, 2, 2, + 2, 2, 2, 2, 0x1A, 0x1D, 0x1B, 0x1A, 0x1D, 0x1B, + 0x1A, 0x1D, 0x1B, 0x1A, 0x1D, 0x1B, 0x17, 0x1D, 0x17, 0x17, + 0x1D, 0x17, 0x17, 0x1D, 0x17, 0x17, 0x1D, 0x17, 0x17, 0x17}; + +// Number of frames at the end of a phoneme devoted to interpolating to next phoneme's final value +//tab45696 +const unsigned char outBlendLength[] = {0, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 3, 2, 4, 4, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 1, 0, 1, 0, 5, + 5, 5, 5, 5, 4, 4, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, + 0, 1, 2, 0, 2, 2, 0, 1, 3, 0, 2, 3, 0, 2, 0xA0, 0xA0}; + +// Number of frames at beginning of a phoneme devoted to interpolating to phoneme's final value +// tab45776 +const unsigned char inBlendLength[] = {0, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3, 1, 2, 3, 2, 1, + 3, 3, 3, 3, 1, 1, 3, 3, 3, 2, 2, 3, 2, 3, 0, 0, + 5, 5, 5, 5, 4, 4, 2, 0, 2, 2, 0, 3, 2, 0, 4, 2, + 0, 3, 2, 0, 2, 2, 0, 2, 3, 0, 3, 3, 0, 3, 0xB0, 0xA0}; + +// Looks like it's used as bit flags +// High bits masked by 248 (11111000) +// +// 32: S* 241 11110001 +// 33: SH 226 11100010 +// 34: F* 211 11010011 +// 35: TH 187 10111011 +// 36: /H 124 01111100 +// 37: /X 149 10010101 +// 38: Z* 1 00000001 +// 39: ZH 2 00000010 +// 40: V* 3 00000011 +// 41: DH 3 00000011 +// 43: ** 114 01110010 +// 45: ** 2 00000010 +// 67: ** 27 00011011 +// 70: ** 25 00011001 +// tab45936 +const unsigned char sampledConsonantFlags[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xF1, 0xE2, 0xD3, 0xBB, 0x7C, 0x95, 1, 2, + 3, 3, 0, 0x72, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x1B, 0, 0, 0x19, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +//tab45056 +unsigned char freq1data[] = { + 0x00, 0x13, 0x13, 0x13, 0x13, 0xA, 0xE, 0x12, 0x18, 0x1A, 0x16, 0x14, 0x10, 0x14, 0xE, 0x12, + 0xE, 0x12, 0x12, 0x10, 0xC, 0xE, 0xA, 0x12, 0xE, 0xA, 8, 6, 6, 6, 6, 0x11, + 6, 6, 6, 6, 0xE, 0x10, 9, 0xA, 8, 0xA, 6, 6, 6, 5, 6, 0, + 0x12, 0x1A, 0x14, 0x1A, 0x12, 0xC, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 0xA, 0xA, 6, 6, 6, 0x2C, 0x13}; + +//tab451356 +unsigned char freq2data[] = {0x00, 0x43, 0x43, 0x43, 0x43, 0x54, 0x48, 0x42, 0x3E, 0x28, + 0x2C, 0x1E, 0x24, 0x2C, 0x48, 0x30, 0x24, 0x1E, 0x32, 0x24, + 0x1C, 0x44, 0x18, 0x32, 0x1E, 0x18, 0x52, 0x2E, 0x36, 0x56, + 0x36, 0x43, 0x49, 0x4F, 0x1A, 0x42, 0x49, 0x25, 0x33, 0x42, + 0x28, 0x2F, 0x4F, 0x4F, 0x42, 0x4F, 0x6E, 0x00, 0x48, 0x26, + 0x1E, 0x2A, 0x1E, 0x22, 0x1A, 0x1A, 0x1A, 0x42, 0x42, 0x42, + 0x6E, 0x6E, 0x6E, 0x54, 0x54, 0x54, 0x1A, 0x1A, 0x1A, 0x42, + 0x42, 0x42, 0x6D, 0x56, 0x6D, 0x54, 0x54, 0x54, 0x7F, 0x7F}; +//tab45216 +unsigned char freq3data[] = {0x00, 0x5B, 0x5B, 0x5B, 0x5B, 0x6E, 0x5D, 0x5B, 0x58, 0x59, + 0x57, 0x58, 0x52, 0x59, 0x5D, 0x3E, 0x52, 0x58, 0x3E, 0x6E, + 0x50, 0x5D, 0x5A, 0x3C, 0x6E, 0x5A, 0x6E, 0x51, 0x79, 0x65, + 0x79, 0x5B, 0x63, 0x6A, 0x51, 0x79, 0x5D, 0x52, 0x5D, 0x67, + 0x4C, 0x5D, 0x65, 0x65, 0x79, 0x65, 0x79, 0x00, 0x5A, 0x58, + 0x58, 0x58, 0x58, 0x52, 0x51, 0x51, 0x51, 0x79, 0x79, 0x79, + 0x70, 0x6E, 0x6E, 0x5E, 0x5E, 0x5E, 0x51, 0x51, 0x51, 0x79, + 0x79, 0x79, 0x65, 0x65, 0x70, 0x5E, 0x5E, 0x5E, 0x08, 0x01}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Reciter +// +//////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char inputtemp[256]; // secure copy of input tab36096 + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Render +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//timetable for more accurate c64 simulation +int timetable[5][5] = { + {162, 167, 167, 127, 128}, + {226, 60, 60, 0, 0}, + {225, 60, 59, 0, 0}, + {200, 0, 0, 54, 55}, + {199, 0, 0, 54, 54}}; + +unsigned oldtimetableindex; + +const unsigned char ampl1data[] = {0, 0, 0, 0, 0, 0xD, 0xD, 0xE, 0xF, 0xF, 0xF, 0xF, + 0xF, 0xC, 0xD, 0xC, 0xF, 0xF, 0xD, 0xD, 0xD, 0xE, 0xD, 0xC, + 0xD, 0xD, 0xD, 0xC, 9, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0xB, 0xB, 0xB, 0xB, 0, 0, 1, 0xB, 0, 2, + 0xE, 0xF, 0xF, 0xF, 0xF, 0xD, 2, 4, 0, 2, 4, 0, + 1, 4, 0, 1, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0xC, 0, 0, 0, 0, 0xF, 0xF}; + +const unsigned char ampl2data[] = { + 0, 0, 0, 0, 0, 0xA, 0xB, 0xD, 0xE, 0xD, 0xC, 0xC, 0xB, 9, 0xB, 0xB, 0xC, 0xC, 0xC, 8, + 8, 0xC, 8, 0xA, 8, 8, 0xA, 3, 9, 6, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, + 3, 4, 0, 0, 0, 5, 0xA, 2, 0xE, 0xD, 0xC, 0xD, 0xC, 8, 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0xA, 0, 0, 0xA, 0, 0, 0}; + +const unsigned char ampl3data[] = {0, 0, 0, 0, 0, 8, 7, 8, 8, 1, 1, 0, 1, 0, 7, 5, + 1, 0, 6, 1, 0, 7, 0, 5, 1, 0, 8, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0xE, 1, + 9, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 5, 0, 0x13, 0x10}; + +//tab42240 +const signed char sinus[256] = { + 0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, + 49, 51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88, + 90, 92, 94, 96, 98, 100, 102, 104, 106, 107, 109, 111, 112, 113, 115, 116, + 117, 118, 120, 121, 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127, + 127, 127, 127, 127, 126, 126, 126, 125, 125, 124, 123, 122, 122, 121, 120, 118, + 117, 116, 115, 113, 112, 111, 109, 107, 106, 104, 102, 100, 98, 96, 94, 92, + 90, 88, 85, 83, 81, 78, 76, 73, 71, 68, 65, 63, 60, 57, 54, 51, + 49, 46, 43, 40, 37, 34, 31, 28, 25, 22, 19, 16, 12, 9, 6, 3, + 0, -3, -6, -9, -12, -16, -19, -22, -25, -28, -31, -34, -37, -40, -43, -46, + -49, -51, -54, -57, -60, -63, -65, -68, -71, -73, -76, -78, -81, -83, -85, -88, + -90, -92, -94, -96, -98, -100, -102, -104, -106, -107, -109, -111, -112, -113, -115, -116, + -117, -118, -120, -121, -122, -122, -123, -124, -125, -125, -126, -126, -126, -127, -127, -127, + -127, -127, -127, -127, -126, -126, -126, -125, -125, -124, -123, -122, -122, -121, -120, -118, + -117, -116, -115, -113, -112, -111, -109, -107, -106, -104, -102, -100, -98, -96, -94, -92, + -90, -88, -85, -83, -81, -78, -76, -73, -71, -68, -65, -63, -60, -57, -54, -51, + -49, -46, -43, -40, -37, -34, -31, -28, -25, -22, -19, -16, -12, -9, -6, -3}; + +//tab42496 +const unsigned char rectangle[] = { + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70}; + +//random data ? +const unsigned char sampleTable[0x500] = { + //00 + + 0x38, + 0x84, + 0x6B, + 0x19, + 0xC6, + 0x63, + 0x18, + 0x86, + 0x73, + 0x98, + 0xC6, + 0xB1, + 0x1C, + 0xCA, + 0x31, + 0x8C, + 0xC7, + 0x31, + 0x88, + 0xC2, + 0x30, + 0x98, + 0x46, + 0x31, + 0x18, + 0xC6, + 0x35, + 0xC, + 0xCA, + 0x31, + 0xC, + 0xC6 + //20 + , + 0x21, + 0x10, + 0x24, + 0x69, + 0x12, + 0xC2, + 0x31, + 0x14, + 0xC4, + 0x71, + 8, + 0x4A, + 0x22, + 0x49, + 0xAB, + 0x6A, + 0xA8, + 0xAC, + 0x49, + 0x51, + 0x32, + 0xD5, + 0x52, + 0x88, + 0x93, + 0x6C, + 0x94, + 0x22, + 0x15, + 0x54, + 0xD2, + 0x25 + //40 + , + 0x96, + 0xD4, + 0x50, + 0xA5, + 0x46, + 0x21, + 8, + 0x85, + 0x6B, + 0x18, + 0xC4, + 0x63, + 0x10, + 0xCE, + 0x6B, + 0x18, + 0x8C, + 0x71, + 0x19, + 0x8C, + 0x63, + 0x35, + 0xC, + 0xC6, + 0x33, + 0x99, + 0xCC, + 0x6C, + 0xB5, + 0x4E, + 0xA2, + 0x99 + //60 + , + 0x46, + 0x21, + 0x28, + 0x82, + 0x95, + 0x2E, + 0xE3, + 0x30, + 0x9C, + 0xC5, + 0x30, + 0x9C, + 0xA2, + 0xB1, + 0x9C, + 0x67, + 0x31, + 0x88, + 0x66, + 0x59, + 0x2C, + 0x53, + 0x18, + 0x84, + 0x67, + 0x50, + 0xCA, + 0xE3, + 0xA, + 0xAC, + 0xAB, + 0x30 + //80 + , + 0xAC, + 0x62, + 0x30, + 0x8C, + 0x63, + 0x10, + 0x94, + 0x62, + 0xB1, + 0x8C, + 0x82, + 0x28, + 0x96, + 0x33, + 0x98, + 0xD6, + 0xB5, + 0x4C, + 0x62, + 0x29, + 0xA5, + 0x4A, + 0xB5, + 0x9C, + 0xC6, + 0x31, + 0x14, + 0xD6, + 0x38, + 0x9C, + 0x4B, + 0xB4 + //A0 + , + 0x86, + 0x65, + 0x18, + 0xAE, + 0x67, + 0x1C, + 0xA6, + 0x63, + 0x19, + 0x96, + 0x23, + 0x19, + 0x84, + 0x13, + 8, + 0xA6, + 0x52, + 0xAC, + 0xCA, + 0x22, + 0x89, + 0x6E, + 0xAB, + 0x19, + 0x8C, + 0x62, + 0x34, + 0xC4, + 0x62, + 0x19, + 0x86, + 0x63 + //C0 + , + 0x18, + 0xC4, + 0x23, + 0x58, + 0xD6, + 0xA3, + 0x50, + 0x42, + 0x54, + 0x4A, + 0xAD, + 0x4A, + 0x25, + 0x11, + 0x6B, + 0x64, + 0x89, + 0x4A, + 0x63, + 0x39, + 0x8A, + 0x23, + 0x31, + 0x2A, + 0xEA, + 0xA2, + 0xA9, + 0x44, + 0xC5, + 0x12, + 0xCD, + 0x42 + //E0 + , + 0x34, + 0x8C, + 0x62, + 0x18, + 0x8C, + 0x63, + 0x11, + 0x48, + 0x66, + 0x31, + 0x9D, + 0x44, + 0x33, + 0x1D, + 0x46, + 0x31, + 0x9C, + 0xC6, + 0xB1, + 0xC, + 0xCD, + 0x32, + 0x88, + 0xC4, + 0x73, + 0x18, + 0x86, + 0x73, + 8, + 0xD6, + 0x63, + 0x58 + //100 + , + 7, + 0x81, + 0xE0, + 0xF0, + 0x3C, + 7, + 0x87, + 0x90, + 0x3C, + 0x7C, + 0xF, + 0xC7, + 0xC0, + 0xC0, + 0xF0, + 0x7C, + 0x1E, + 7, + 0x80, + 0x80, + 0, + 0x1C, + 0x78, + 0x70, + 0xF1, + 0xC7, + 0x1F, + 0xC0, + 0xC, + 0xFE, + 0x1C, + 0x1F + //120 + , + 0x1F, + 0xE, + 0xA, + 0x7A, + 0xC0, + 0x71, + 0xF2, + 0x83, + 0x8F, + 3, + 0xF, + 0xF, + 0xC, + 0, + 0x79, + 0xF8, + 0x61, + 0xE0, + 0x43, + 0xF, + 0x83, + 0xE7, + 0x18, + 0xF9, + 0xC1, + 0x13, + 0xDA, + 0xE9, + 0x63, + 0x8F, + 0xF, + 0x83 + //140 + , + 0x83, + 0x87, + 0xC3, + 0x1F, + 0x3C, + 0x70, + 0xF0, + 0xE1, + 0xE1, + 0xE3, + 0x87, + 0xB8, + 0x71, + 0xE, + 0x20, + 0xE3, + 0x8D, + 0x48, + 0x78, + 0x1C, + 0x93, + 0x87, + 0x30, + 0xE1, + 0xC1, + 0xC1, + 0xE4, + 0x78, + 0x21, + 0x83, + 0x83, + 0xC3 + //160 + , + 0x87, + 6, + 0x39, + 0xE5, + 0xC3, + 0x87, + 7, + 0xE, + 0x1C, + 0x1C, + 0x70, + 0xF4, + 0x71, + 0x9C, + 0x60, + 0x36, + 0x32, + 0xC3, + 0x1E, + 0x3C, + 0xF3, + 0x8F, + 0xE, + 0x3C, + 0x70, + 0xE3, + 0xC7, + 0x8F, + 0xF, + 0xF, + 0xE, + 0x3C + //180 + , + 0x78, + 0xF0, + 0xE3, + 0x87, + 6, + 0xF0, + 0xE3, + 7, + 0xC1, + 0x99, + 0x87, + 0xF, + 0x18, + 0x78, + 0x70, + 0x70, + 0xFC, + 0xF3, + 0x10, + 0xB1, + 0x8C, + 0x8C, + 0x31, + 0x7C, + 0x70, + 0xE1, + 0x86, + 0x3C, + 0x64, + 0x6C, + 0xB0, + 0xE1 + //1A0 + , + 0xE3, + 0xF, + 0x23, + 0x8F, + 0xF, + 0x1E, + 0x3E, + 0x38, + 0x3C, + 0x38, + 0x7B, + 0x8F, + 7, + 0xE, + 0x3C, + 0xF4, + 0x17, + 0x1E, + 0x3C, + 0x78, + 0xF2, + 0x9E, + 0x72, + 0x49, + 0xE3, + 0x25, + 0x36, + 0x38, + 0x58, + 0x39, + 0xE2, + 0xDE + //1C0 + , + 0x3C, + 0x78, + 0x78, + 0xE1, + 0xC7, + 0x61, + 0xE1, + 0xE1, + 0xB0, + 0xF0, + 0xF0, + 0xC3, + 0xC7, + 0xE, + 0x38, + 0xC0, + 0xF0, + 0xCE, + 0x73, + 0x73, + 0x18, + 0x34, + 0xB0, + 0xE1, + 0xC7, + 0x8E, + 0x1C, + 0x3C, + 0xF8, + 0x38, + 0xF0, + 0xE1 + //1E0 + , + 0xC1, + 0x8B, + 0x86, + 0x8F, + 0x1C, + 0x78, + 0x70, + 0xF0, + 0x78, + 0xAC, + 0xB1, + 0x8F, + 0x39, + 0x31, + 0xDB, + 0x38, + 0x61, + 0xC3, + 0xE, + 0xE, + 0x38, + 0x78, + 0x73, + 0x17, + 0x1E, + 0x39, + 0x1E, + 0x38, + 0x64, + 0xE1, + 0xF1, + 0xC1 + //200 + , + 0x4E, + 0xF, + 0x40, + 0xA2, + 2, + 0xC5, + 0x8F, + 0x81, + 0xA1, + 0xFC, + 0x12, + 8, + 0x64, + 0xE0, + 0x3C, + 0x22, + 0xE0, + 0x45, + 7, + 0x8E, + 0xC, + 0x32, + 0x90, + 0xF0, + 0x1F, + 0x20, + 0x49, + 0xE0, + 0xF8, + 0xC, + 0x60, + 0xF0 + //220 + , + 0x17, + 0x1A, + 0x41, + 0xAA, + 0xA4, + 0xD0, + 0x8D, + 0x12, + 0x82, + 0x1E, + 0x1E, + 3, + 0xF8, + 0x3E, + 3, + 0xC, + 0x73, + 0x80, + 0x70, + 0x44, + 0x26, + 3, + 0x24, + 0xE1, + 0x3E, + 4, + 0x4E, + 4, + 0x1C, + 0xC1, + 9, + 0xCC + //240 + , + 0x9E, + 0x90, + 0x21, + 7, + 0x90, + 0x43, + 0x64, + 0xC0, + 0xF, + 0xC6, + 0x90, + 0x9C, + 0xC1, + 0x5B, + 3, + 0xE2, + 0x1D, + 0x81, + 0xE0, + 0x5E, + 0x1D, + 3, + 0x84, + 0xB8, + 0x2C, + 0xF, + 0x80, + 0xB1, + 0x83, + 0xE0, + 0x30, + 0x41 + //260 + , + 0x1E, + 0x43, + 0x89, + 0x83, + 0x50, + 0xFC, + 0x24, + 0x2E, + 0x13, + 0x83, + 0xF1, + 0x7C, + 0x4C, + 0x2C, + 0xC9, + 0xD, + 0x83, + 0xB0, + 0xB5, + 0x82, + 0xE4, + 0xE8, + 6, + 0x9C, + 7, + 0xA0, + 0x99, + 0x1D, + 7, + 0x3E, + 0x82, + 0x8F + //280 + , + 0x70, + 0x30, + 0x74, + 0x40, + 0xCA, + 0x10, + 0xE4, + 0xE8, + 0xF, + 0x92, + 0x14, + 0x3F, + 6, + 0xF8, + 0x84, + 0x88, + 0x43, + 0x81, + 0xA, + 0x34, + 0x39, + 0x41, + 0xC6, + 0xE3, + 0x1C, + 0x47, + 3, + 0xB0, + 0xB8, + 0x13, + 0xA, + 0xC2 + //2A0 + , + 0x64, + 0xF8, + 0x18, + 0xF9, + 0x60, + 0xB3, + 0xC0, + 0x65, + 0x20, + 0x60, + 0xA6, + 0x8C, + 0xC3, + 0x81, + 0x20, + 0x30, + 0x26, + 0x1E, + 0x1C, + 0x38, + 0xD3, + 1, + 0xB0, + 0x26, + 0x40, + 0xF4, + 0xB, + 0xC3, + 0x42, + 0x1F, + 0x85, + 0x32 + //2C0 + , + 0x26, + 0x60, + 0x40, + 0xC9, + 0xCB, + 1, + 0xEC, + 0x11, + 0x28, + 0x40, + 0xFA, + 4, + 0x34, + 0xE0, + 0x70, + 0x4C, + 0x8C, + 0x1D, + 7, + 0x69, + 3, + 0x16, + 0xC8, + 4, + 0x23, + 0xE8, + 0xC6, + 0x9A, + 0xB, + 0x1A, + 3, + 0xE0 + //2E0 + , + 0x76, + 6, + 5, + 0xCF, + 0x1E, + 0xBC, + 0x58, + 0x31, + 0x71, + 0x66, + 0, + 0xF8, + 0x3F, + 4, + 0xFC, + 0xC, + 0x74, + 0x27, + 0x8A, + 0x80, + 0x71, + 0xC2, + 0x3A, + 0x26, + 6, + 0xC0, + 0x1F, + 5, + 0xF, + 0x98, + 0x40, + 0xAE + //300 + , + 1, + 0x7F, + 0xC0, + 7, + 0xFF, + 0, + 0xE, + 0xFE, + 0, + 3, + 0xDF, + 0x80, + 3, + 0xEF, + 0x80, + 0x1B, + 0xF1, + 0xC2, + 0, + 0xE7, + 0xE0, + 0x18, + 0xFC, + 0xE0, + 0x21, + 0xFC, + 0x80, + 0x3C, + 0xFC, + 0x40, + 0xE, + 0x7E + //320 + , + 0, + 0x3F, + 0x3E, + 0, + 0xF, + 0xFE, + 0, + 0x1F, + 0xFF, + 0, + 0x3E, + 0xF0, + 7, + 0xFC, + 0, + 0x7E, + 0x10, + 0x3F, + 0xFF, + 0, + 0x3F, + 0x38, + 0xE, + 0x7C, + 1, + 0x87, + 0xC, + 0xFC, + 0xC7, + 0, + 0x3E, + 4 + //340 + , + 0xF, + 0x3E, + 0x1F, + 0xF, + 0xF, + 0x1F, + 0xF, + 2, + 0x83, + 0x87, + 0xCF, + 3, + 0x87, + 0xF, + 0x3F, + 0xC0, + 7, + 0x9E, + 0x60, + 0x3F, + 0xC0, + 3, + 0xFE, + 0, + 0x3F, + 0xE0, + 0x77, + 0xE1, + 0xC0, + 0xFE, + 0xE0, + 0xC3 + //360 + , + 0xE0, + 1, + 0xDF, + 0xF8, + 3, + 7, + 0, + 0x7E, + 0x70, + 0, + 0x7C, + 0x38, + 0x18, + 0xFE, + 0xC, + 0x1E, + 0x78, + 0x1C, + 0x7C, + 0x3E, + 0xE, + 0x1F, + 0x1E, + 0x1E, + 0x3E, + 0, + 0x7F, + 0x83, + 7, + 0xDB, + 0x87, + 0x83 + //380 + , + 7, + 0xC7, + 7, + 0x10, + 0x71, + 0xFF, + 0, + 0x3F, + 0xE2, + 1, + 0xE0, + 0xC1, + 0xC3, + 0xE1, + 0, + 0x7F, + 0xC0, + 5, + 0xF0, + 0x20, + 0xF8, + 0xF0, + 0x70, + 0xFE, + 0x78, + 0x79, + 0xF8, + 2, + 0x3F, + 0xC, + 0x8F, + 3 + //3a0 + , + 0xF, + 0x9F, + 0xE0, + 0xC1, + 0xC7, + 0x87, + 3, + 0xC3, + 0xC3, + 0xB0, + 0xE1, + 0xE1, + 0xC1, + 0xE3, + 0xE0, + 0x71, + 0xF0, + 0, + 0xFC, + 0x70, + 0x7C, + 0xC, + 0x3E, + 0x38, + 0xE, + 0x1C, + 0x70, + 0xC3, + 0xC7, + 3, + 0x81, + 0xC1 + //3c0 + , + 0xC7, + 0xE7, + 0, + 0xF, + 0xC7, + 0x87, + 0x19, + 9, + 0xEF, + 0xC4, + 0x33, + 0xE0, + 0xC1, + 0xFC, + 0xF8, + 0x70, + 0xF0, + 0x78, + 0xF8, + 0xF0, + 0x61, + 0xC7, + 0, + 0x1F, + 0xF8, + 1, + 0x7C, + 0xF8, + 0xF0, + 0x78, + 0x70, + 0x3C + //3e0 + , + 0x7C, + 0xCE, + 0xE, + 0x21, + 0x83, + 0xCF, + 8, + 7, + 0x8F, + 8, + 0xC1, + 0x87, + 0x8F, + 0x80, + 0xC7, + 0xE3, + 0, + 7, + 0xF8, + 0xE0, + 0xEF, + 0, + 0x39, + 0xF7, + 0x80, + 0xE, + 0xF8, + 0xE1, + 0xE3, + 0xF8, + 0x21, + 0x9F + //400 + , + 0xC0, + 0xFF, + 3, + 0xF8, + 7, + 0xC0, + 0x1F, + 0xF8, + 0xC4, + 4, + 0xFC, + 0xC4, + 0xC1, + 0xBC, + 0x87, + 0xF0, + 0xF, + 0xC0, + 0x7F, + 5, + 0xE0, + 0x25, + 0xEC, + 0xC0, + 0x3E, + 0x84, + 0x47, + 0xF0, + 0x8E, + 3, + 0xF8, + 3 + //420 + , + 0xFB, + 0xC0, + 0x19, + 0xF8, + 7, + 0x9C, + 0xC, + 0x17, + 0xF8, + 7, + 0xE0, + 0x1F, + 0xA1, + 0xFC, + 0xF, + 0xFC, + 1, + 0xF0, + 0x3F, + 0, + 0xFE, + 3, + 0xF0, + 0x1F, + 0, + 0xFD, + 0, + 0xFF, + 0x88, + 0xD, + 0xF9, + 1 + //440 + , + 0xFF, + 0, + 0x70, + 7, + 0xC0, + 0x3E, + 0x42, + 0xF3, + 0xD, + 0xC4, + 0x7F, + 0x80, + 0xFC, + 7, + 0xF0, + 0x5E, + 0xC0, + 0x3F, + 0, + 0x78, + 0x3F, + 0x81, + 0xFF, + 1, + 0xF8, + 1, + 0xC3, + 0xE8, + 0xC, + 0xE4, + 0x64, + 0x8F + ////460 + , + 0xE4, + 0xF, + 0xF0, + 7, + 0xF0, + 0xC2, + 0x1F, + 0, + 0x7F, + 0xC0, + 0x6F, + 0x80, + 0x7E, + 3, + 0xF8, + 7, + 0xF0, + 0x3F, + 0xC0, + 0x78, + 0xF, + 0x82, + 7, + 0xFE, + 0x22, + 0x77, + 0x70, + 2, + 0x76, + 3, + 0xFE, + 0 + //480 + , + 0xFE, + 0x67, + 0, + 0x7C, + 0xC7, + 0xF1, + 0x8E, + 0xC6, + 0x3B, + 0xE0, + 0x3F, + 0x84, + 0xF3, + 0x19, + 0xD8, + 3, + 0x99, + 0xFC, + 9, + 0xB8, + 0xF, + 0xF8, + 0, + 0x9D, + 0x24, + 0x61, + 0xF9, + 0xD, + 0, + 0xFD, + 3, + 0xF0 + //4a0 + , + 0x1F, + 0x90, + 0x3F, + 1, + 0xF8, + 0x1F, + 0xD0, + 0xF, + 0xF8, + 0x37, + 1, + 0xF8, + 7, + 0xF0, + 0xF, + 0xC0, + 0x3F, + 0, + 0xFE, + 3, + 0xF8, + 0xF, + 0xC0, + 0x3F, + 0, + 0xFA, + 3, + 0xF0, + 0xF, + 0x80, + 0xFF, + 1 + //4c0 + , + 0xB8, + 7, + 0xF0, + 1, + 0xFC, + 1, + 0xBC, + 0x80, + 0x13, + 0x1E, + 0, + 0x7F, + 0xE1, + 0x40, + 0x7F, + 0xA0, + 0x7F, + 0xB0, + 0, + 0x3F, + 0xC0, + 0x1F, + 0xC0, + 0x38, + 0xF, + 0xF0, + 0x1F, + 0x80, + 0xFF, + 1, + 0xFC, + 3 + //4e0 + , + 0xF1, + 0x7E, + 1, + 0xFE, + 1, + 0xF0, + 0xFF, + 0, + 0x7F, + 0xC0, + 0x1D, + 7, + 0xF0, + 0xF, + 0xC0, + 0x7E, + 6, + 0xE0, + 7, + 0xE0, + 0xF, + 0xF8, + 6, + 0xC1, + 0xFE, + 1, + 0xFC, + 3, + 0xE0, + 0xF, + 0, + 0xFC}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Render +// +//////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char pitches[256]; // tab43008 + +unsigned char frequency1[256]; +unsigned char frequency2[256]; +unsigned char frequency3[256]; + +unsigned char amplitude1[256]; +unsigned char amplitude2[256]; +unsigned char amplitude3[256]; + +unsigned char sampledConsonantFlag[256]; // tab44800 + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Sam +// +//////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char stress[256]; //numbers from 0 to 8 +unsigned char phonemeLength[256]; //tab40160 +unsigned char phonemeindex[256]; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// ReciterTabs +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//some flags +const unsigned char tab36376[] = { + 0, 0, 0, 0, 0, 0, 0, 0, // 0-7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8-15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 130, // ' ', '!' + 0, 0, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 2, 2, 2, 2, 2, 192, 168, 176, 172, 192, 160, 184, // '@', 'A' + 160, 192, 188, 160, 172, 168, 172, 192, 160, 160, 172, 180, 164, 192, 168, 168, + 176, 192, 188, 0, 0, 0, 2, 0, // 'X', 'Y', 'Z', '[', + 32, 32, 155, 32, 192, 185, 32, 205, 163, 76, 138, 142}; + +const unsigned char rules[] = { + ']', 'A' | 0x80, ' ', '(', 'A', '.', ')', '=', + 'E', 'H', '4', 'Y', '.', ' ' | 0x80, '(', 'A', + ')', ' ', '=', 'A', 'H' | 0x80, ' ', '(', 'A', + 'R', 'E', ')', ' ', '=', 'A', 'A', 'R' | 0x80, + ' ', '(', 'A', 'R', ')', 'O', '=', 'A', + 'X', 'R' | 0x80, '(', 'A', 'R', ')', '#', '=', + 'E', 'H', '4', 'R' | 0x80, ' ', '^', '(', 'A', + 'S', ')', '#', '=', 'E', 'Y', '4', 'S' | 0x80, + '(', 'A', ')', 'W', 'A', '=', 'A', 'X' | 0x80, + '(', 'A', 'W', ')', '=', 'A', 'O', '5' | 0x80, + ' ', ':', '(', 'A', 'N', 'Y', ')', '=', + 'E', 'H', '4', 'N', 'I', 'Y' | 0x80, '(', 'A', + ')', '^', '+', '#', '=', 'E', 'Y', '5' | 0x80, + '#', ':', '(', 'A', 'L', 'L', 'Y', ')', + '=', 'U', 'L', 'I', 'Y' | 0x80, ' ', '(', 'A', + 'L', ')', '#', '=', 'U', 'L' | 0x80, '(', 'A', + 'G', 'A', 'I', 'N', ')', '=', 'A', 'X', + 'G', 'E', 'H', '4', 'N' | 0x80, '#', ':', '(', + 'A', 'G', ')', 'E', '=', 'I', 'H', 'J' | 0x80, + '(', 'A', ')', '^', '%', '=', 'E', 'Y' | 0x80, + '(', 'A', ')', '^', '+', ':', '#', '=', + 'A', 'E' | 0x80, ' ', ':', '(', 'A', ')', '^', + '+', ' ', '=', 'E', 'Y', '4' | 0x80, ' ', '(', + 'A', 'R', 'R', ')', '=', 'A', 'X', 'R' | 0x80, + '(', 'A', 'R', 'R', ')', '=', 'A', 'E', + '4', 'R' | 0x80, ' ', '^', '(', 'A', 'R', ')', + ' ', '=', 'A', 'A', '5', 'R' | 0x80, '(', 'A', + 'R', ')', '=', 'A', 'A', '5', 'R' | 0x80, '(', + 'A', 'I', 'R', ')', '=', 'E', 'H', '4', + 'R' | 0x80, '(', 'A', 'I', ')', '=', 'E', 'Y', + '4' | 0x80, '(', 'A', 'Y', ')', '=', 'E', 'Y', + '5' | 0x80, '(', 'A', 'U', ')', '=', 'A', 'O', + '4' | 0x80, '#', ':', '(', 'A', 'L', ')', ' ', + '=', 'U', 'L' | 0x80, '#', ':', '(', 'A', 'L', + 'S', ')', ' ', '=', 'U', 'L', 'Z' | 0x80, '(', + 'A', 'L', 'K', ')', '=', 'A', 'O', '4', + 'K' | 0x80, '(', 'A', 'L', ')', '^', '=', 'A', + 'O', 'L' | 0x80, ' ', ':', '(', 'A', 'B', 'L', + 'E', ')', '=', 'E', 'Y', '4', 'B', 'U', + 'L' | 0x80, '(', 'A', 'B', 'L', 'E', ')', '=', + 'A', 'X', 'B', 'U', 'L' | 0x80, '(', 'A', ')', + 'V', 'O', '=', 'E', 'Y', '4' | 0x80, '(', 'A', + 'N', 'G', ')', '+', '=', 'E', 'Y', '4', + 'N', 'J' | 0x80, '(', 'A', 'T', 'A', 'R', 'I', + ')', '=', 'A', 'H', 'T', 'A', 'A', '4', + 'R', 'I', 'Y' | 0x80, '(', 'A', ')', 'T', 'O', + 'M', '=', 'A', 'E' | 0x80, '(', 'A', ')', 'T', + 'T', 'I', '=', 'A', 'E' | 0x80, ' ', '(', 'A', + 'T', ')', ' ', '=', 'A', 'E', 'T' | 0x80, ' ', + '(', 'A', ')', 'T', '=', 'A', 'H' | 0x80, '(', + 'A', ')', '=', 'A', 'E' | 0x80, + + ']', 'B' | 0x80, ' ', '(', 'B', ')', ' ', '=', + 'B', 'I', 'Y', '4' | 0x80, ' ', '(', 'B', 'E', + ')', '^', '#', '=', 'B', 'I', 'H' | 0x80, '(', + 'B', 'E', 'I', 'N', 'G', ')', '=', 'B', + 'I', 'Y', '4', 'I', 'H', 'N', 'X' | 0x80, ' ', + '(', 'B', 'O', 'T', 'H', ')', ' ', '=', + 'B', 'O', 'W', '4', 'T', 'H' | 0x80, ' ', '(', + 'B', 'U', 'S', ')', '#', '=', 'B', 'I', + 'H', '4', 'Z' | 0x80, '(', 'B', 'R', 'E', 'A', + 'K', ')', '=', 'B', 'R', 'E', 'Y', '5', + 'K' | 0x80, '(', 'B', 'U', 'I', 'L', ')', '=', + 'B', 'I', 'H', '4', 'L' | 0x80, '(', 'B', ')', + '=', 'B' | 0x80, + + ']', 'C' | 0x80, ' ', '(', 'C', ')', ' ', '=', + 'S', 'I', 'Y', '4' | 0x80, ' ', '(', 'C', 'H', + ')', '^', '=', 'K' | 0x80, '^', 'E', '(', 'C', + 'H', ')', '=', 'K' | 0x80, '(', 'C', 'H', 'A', + ')', 'R', '#', '=', 'K', 'E', 'H', '5' | 0x80, + '(', 'C', 'H', ')', '=', 'C', 'H' | 0x80, ' ', + 'S', '(', 'C', 'I', ')', '#', '=', 'S', + 'A', 'Y', '4' | 0x80, '(', 'C', 'I', ')', 'A', + '=', 'S', 'H' | 0x80, '(', 'C', 'I', ')', 'O', + '=', 'S', 'H' | 0x80, '(', 'C', 'I', ')', 'E', + 'N', '=', 'S', 'H' | 0x80, '(', 'C', 'I', 'T', + 'Y', ')', '=', 'S', 'I', 'H', 'T', 'I', + 'Y' | 0x80, '(', 'C', ')', '+', '=', 'S' | 0x80, '(', + 'C', 'K', ')', '=', 'K' | 0x80, '(', 'C', 'O', + 'M', 'M', 'O', 'D', 'O', 'R', 'E', ')', + '=', 'K', 'A', 'A', '4', 'M', 'A', 'H', + 'D', 'O', 'H', 'R' | 0x80, '(', 'C', 'O', 'M', + ')', '=', 'K', 'A', 'H', 'M' | 0x80, '(', 'C', + 'U', 'I', 'T', ')', '=', 'K', 'I', 'H', + 'T' | 0x80, '(', 'C', 'R', 'E', 'A', ')', '=', + 'K', 'R', 'I', 'Y', 'E', 'Y' | 0x80, '(', 'C', + ')', '=', 'K' | 0x80, + + ']', 'D' | 0x80, ' ', '(', 'D', ')', ' ', '=', + 'D', 'I', 'Y', '4' | 0x80, ' ', '(', 'D', 'R', + '.', ')', ' ', '=', 'D', 'A', 'A', '4', + 'K', 'T', 'E', 'R' | 0x80, '#', ':', '(', 'D', + 'E', 'D', ')', ' ', '=', 'D', 'I', 'H', + 'D' | 0x80, '.', 'E', '(', 'D', ')', ' ', '=', + 'D' | 0x80, '#', ':', '^', 'E', '(', 'D', ')', + ' ', '=', 'T' | 0x80, ' ', '(', 'D', 'E', ')', + '^', '#', '=', 'D', 'I', 'H' | 0x80, ' ', '(', + 'D', 'O', ')', ' ', '=', 'D', 'U', 'W' | 0x80, + ' ', '(', 'D', 'O', 'E', 'S', ')', '=', + 'D', 'A', 'H', 'Z' | 0x80, '(', 'D', 'O', 'N', + 'E', ')', ' ', '=', 'D', 'A', 'H', '5', + 'N' | 0x80, '(', 'D', 'O', 'I', 'N', 'G', ')', + '=', 'D', 'U', 'W', '4', 'I', 'H', 'N', + 'X' | 0x80, ' ', '(', 'D', 'O', 'W', ')', '=', + 'D', 'A', 'W' | 0x80, '#', '(', 'D', 'U', ')', + 'A', '=', 'J', 'U', 'W' | 0x80, '#', '(', 'D', + 'U', ')', '^', '#', '=', 'J', 'A', 'X' | 0x80, + '(', 'D', ')', '=', 'D' | 0x80, + + ']', 'E' | 0x80, ' ', '(', 'E', ')', ' ', '=', + 'I', 'Y', 'I', 'Y', '4' | 0x80, '#', ':', '(', + 'E', ')', ' ', '=' | 0x80, '\'', ':', '^', '(', + 'E', ')', ' ', '=' | 0x80, ' ', ':', '(', 'E', + ')', ' ', '=', 'I', 'Y' | 0x80, '#', '(', 'E', + 'D', ')', ' ', '=', 'D' | 0x80, '#', ':', '(', + 'E', ')', 'D', ' ', '=' | 0x80, '(', 'E', 'V', + ')', 'E', 'R', '=', 'E', 'H', '4', 'V' | 0x80, + '(', 'E', ')', '^', '%', '=', 'I', 'Y', + '4' | 0x80, '(', 'E', 'R', 'I', ')', '#', '=', + 'I', 'Y', '4', 'R', 'I', 'Y' | 0x80, '(', 'E', + 'R', 'I', ')', '=', 'E', 'H', '4', 'R', + 'I', 'H' | 0x80, '#', ':', '(', 'E', 'R', ')', + '#', '=', 'E', 'R' | 0x80, '(', 'E', 'R', 'R', + 'O', 'R', ')', '=', 'E', 'H', '4', 'R', + 'O', 'H', 'R' | 0x80, '(', 'E', 'R', 'A', 'S', + 'E', ')', '=', 'I', 'H', 'R', 'E', 'Y', + '5', 'S' | 0x80, '(', 'E', 'R', ')', '#', '=', + 'E', 'H', 'R' | 0x80, '(', 'E', 'R', ')', '=', + 'E', 'R' | 0x80, ' ', '(', 'E', 'V', 'E', 'N', + ')', '=', 'I', 'Y', 'V', 'E', 'H', 'N' | 0x80, + '#', ':', '(', 'E', ')', 'W', '=' | 0x80, '@', + '(', 'E', 'W', ')', '=', 'U', 'W' | 0x80, '(', + 'E', 'W', ')', '=', 'Y', 'U', 'W' | 0x80, '(', + 'E', ')', 'O', '=', 'I', 'Y' | 0x80, '#', ':', + '&', '(', 'E', 'S', ')', ' ', '=', 'I', + 'H', 'Z' | 0x80, '#', ':', '(', 'E', ')', 'S', + ' ', '=' | 0x80, '#', ':', '(', 'E', 'L', 'Y', + ')', ' ', '=', 'L', 'I', 'Y' | 0x80, '#', ':', + '(', 'E', 'M', 'E', 'N', 'T', ')', '=', + 'M', 'E', 'H', 'N', 'T' | 0x80, '(', 'E', 'F', + 'U', 'L', ')', '=', 'F', 'U', 'H', 'L' | 0x80, + '(', 'E', 'E', ')', '=', 'I', 'Y', '4' | 0x80, + '(', 'E', 'A', 'R', 'N', ')', '=', 'E', + 'R', '5', 'N' | 0x80, ' ', '(', 'E', 'A', 'R', + ')', '^', '=', 'E', 'R', '5' | 0x80, '(', 'E', + 'A', 'D', ')', '=', 'E', 'H', 'D' | 0x80, '#', + ':', '(', 'E', 'A', ')', ' ', '=', 'I', + 'Y', 'A', 'X' | 0x80, '(', 'E', 'A', ')', 'S', + 'U', '=', 'E', 'H', '5' | 0x80, '(', 'E', 'A', + ')', '=', 'I', 'Y', '5' | 0x80, '(', 'E', 'I', + 'G', 'H', ')', '=', 'E', 'Y', '4' | 0x80, '(', + 'E', 'I', ')', '=', 'I', 'Y', '4' | 0x80, ' ', + '(', 'E', 'Y', 'E', ')', '=', 'A', 'Y', + '4' | 0x80, '(', 'E', 'Y', ')', '=', 'I', 'Y' | 0x80, + '(', 'E', 'U', ')', '=', 'Y', 'U', 'W', + '5' | 0x80, '(', 'E', 'Q', 'U', 'A', 'L', ')', + '=', 'I', 'Y', '4', 'K', 'W', 'U', 'L' | 0x80, + '(', 'E', ')', '=', 'E', 'H' | 0x80, + + ']', 'F' | 0x80, ' ', '(', 'F', ')', ' ', '=', + 'E', 'H', '4', 'F' | 0x80, '(', 'F', 'U', 'L', + ')', '=', 'F', 'U', 'H', 'L' | 0x80, '(', 'F', + 'R', 'I', 'E', 'N', 'D', ')', '=', 'F', + 'R', 'E', 'H', '5', 'N', 'D' | 0x80, '(', 'F', + 'A', 'T', 'H', 'E', 'R', ')', '=', 'F', + 'A', 'A', '4', 'D', 'H', 'E', 'R' | 0x80, '(', + 'F', ')', 'F', '=' | 0x80, '(', 'F', ')', '=', + 'F' | 0x80, + + ']', 'G' | 0x80, ' ', '(', 'G', ')', ' ', '=', + 'J', 'I', 'Y', '4' | 0x80, '(', 'G', 'I', 'V', + ')', '=', 'G', 'I', 'H', '5', 'V' | 0x80, ' ', + '(', 'G', ')', 'I', '^', '=', 'G' | 0x80, '(', + 'G', 'E', ')', 'T', '=', 'G', 'E', 'H', + '5' | 0x80, 'S', 'U', '(', 'G', 'G', 'E', 'S', + ')', '=', 'G', 'J', 'E', 'H', '4', 'S' | 0x80, + '(', 'G', 'G', ')', '=', 'G' | 0x80, ' ', 'B', + '#', '(', 'G', ')', '=', 'G' | 0x80, '(', 'G', + ')', '+', '=', 'J' | 0x80, '(', 'G', 'R', 'E', + 'A', 'T', ')', '=', 'G', 'R', 'E', 'Y', + '4', 'T' | 0x80, '(', 'G', 'O', 'N', ')', 'E', + '=', 'G', 'A', 'O', '5', 'N' | 0x80, '#', '(', + 'G', 'H', ')', '=' | 0x80, ' ', '(', 'G', 'N', + ')', '=', 'N' | 0x80, '(', 'G', ')', '=', 'G' | 0x80, + + ']', 'H' | 0x80, ' ', '(', 'H', ')', ' ', '=', + 'E', 'Y', '4', 'C', 'H' | 0x80, ' ', '(', 'H', + 'A', 'V', ')', '=', '/', 'H', 'A', 'E', + '6', 'V' | 0x80, ' ', '(', 'H', 'E', 'R', 'E', + ')', '=', '/', 'H', 'I', 'Y', 'R' | 0x80, ' ', + '(', 'H', 'O', 'U', 'R', ')', '=', 'A', + 'W', '5', 'E', 'R' | 0x80, '(', 'H', 'O', 'W', + ')', '=', '/', 'H', 'A', 'W' | 0x80, '(', 'H', + ')', '#', '=', '/', 'H' | 0x80, '(', 'H', ')', + '=' | 0x80, + + ']', 'I' | 0x80, ' ', '(', 'I', 'N', ')', '=', + 'I', 'H', 'N' | 0x80, ' ', '(', 'I', ')', ' ', + '=', 'A', 'Y', '4' | 0x80, '(', 'I', ')', ' ', + '=', 'A', 'Y' | 0x80, '(', 'I', 'N', ')', 'D', + '=', 'A', 'Y', '5', 'N' | 0x80, 'S', 'E', 'M', + '(', 'I', ')', '=', 'I', 'Y' | 0x80, ' ', 'A', + 'N', 'T', '(', 'I', ')', '=', 'A', 'Y' | 0x80, + '(', 'I', 'E', 'R', ')', '=', 'I', 'Y', + 'E', 'R' | 0x80, '#', ':', 'R', '(', 'I', 'E', + 'D', ')', ' ', '=', 'I', 'Y', 'D' | 0x80, '(', + 'I', 'E', 'D', ')', ' ', '=', 'A', 'Y', + '5', 'D' | 0x80, '(', 'I', 'E', 'N', ')', '=', + 'I', 'Y', 'E', 'H', 'N' | 0x80, '(', 'I', 'E', + ')', 'T', '=', 'A', 'Y', '4', 'E', 'H' | 0x80, + '(', 'I', '\'', ')', '=', 'A', 'Y', '5' | 0x80, + ' ', ':', '(', 'I', ')', '^', '%', '=', + 'A', 'Y', '5' | 0x80, ' ', ':', '(', 'I', 'E', + ')', ' ', '=', 'A', 'Y', '4' | 0x80, '(', 'I', + ')', '%', '=', 'I', 'Y' | 0x80, '(', 'I', 'E', + ')', '=', 'I', 'Y', '4' | 0x80, ' ', '(', 'I', + 'D', 'E', 'A', ')', '=', 'A', 'Y', 'D', + 'I', 'Y', '5', 'A', 'H' | 0x80, '(', 'I', ')', + '^', '+', ':', '#', '=', 'I', 'H' | 0x80, '(', + 'I', 'R', ')', '#', '=', 'A', 'Y', 'R' | 0x80, + '(', 'I', 'Z', ')', '%', '=', 'A', 'Y', + 'Z' | 0x80, '(', 'I', 'S', ')', '%', '=', 'A', + 'Y', 'Z' | 0x80, 'I', '^', '(', 'I', ')', '^', + '#', '=', 'I', 'H' | 0x80, '+', '^', '(', 'I', + ')', '^', '+', '=', 'A', 'Y' | 0x80, '#', ':', + '^', '(', 'I', ')', '^', '+', '=', 'I', + 'H' | 0x80, '(', 'I', ')', '^', '+', '=', 'A', + 'Y' | 0x80, '(', 'I', 'R', ')', '=', 'E', 'R' | 0x80, + '(', 'I', 'G', 'H', ')', '=', 'A', 'Y', + '4' | 0x80, '(', 'I', 'L', 'D', ')', '=', 'A', + 'Y', '5', 'L', 'D' | 0x80, ' ', '(', 'I', 'G', + 'N', ')', '=', 'I', 'H', 'G', 'N' | 0x80, '(', + 'I', 'G', 'N', ')', ' ', '=', 'A', 'Y', + '4', 'N' | 0x80, '(', 'I', 'G', 'N', ')', '^', + '=', 'A', 'Y', '4', 'N' | 0x80, '(', 'I', 'G', + 'N', ')', '%', '=', 'A', 'Y', '4', 'N' | 0x80, + '(', 'I', 'C', 'R', 'O', ')', '=', 'A', + 'Y', '4', 'K', 'R', 'O', 'H' | 0x80, '(', 'I', + 'Q', 'U', 'E', ')', '=', 'I', 'Y', '4', + 'K' | 0x80, '(', 'I', ')', '=', 'I', 'H' | 0x80, + + ']', 'J' | 0x80, ' ', '(', 'J', ')', ' ', '=', + 'J', 'E', 'Y', '4' | 0x80, '(', 'J', ')', '=', + 'J' | 0x80, + + ']', 'K' | 0x80, ' ', '(', 'K', ')', ' ', '=', + 'K', 'E', 'Y', '4' | 0x80, ' ', '(', 'K', ')', + 'N', '=' | 0x80, '(', 'K', ')', '=', 'K' | 0x80, + + ']', 'L' | 0x80, ' ', '(', 'L', ')', ' ', '=', + 'E', 'H', '4', 'L' | 0x80, '(', 'L', 'O', ')', + 'C', '#', '=', 'L', 'O', 'W' | 0x80, 'L', '(', + 'L', ')', '=' | 0x80, '#', ':', '^', '(', 'L', + ')', '%', '=', 'U', 'L' | 0x80, '(', 'L', 'E', + 'A', 'D', ')', '=', 'L', 'I', 'Y', 'D' | 0x80, + ' ', '(', 'L', 'A', 'U', 'G', 'H', ')', + '=', 'L', 'A', 'E', '4', 'F' | 0x80, '(', 'L', + ')', '=', 'L' | 0x80, + + ']', 'M' | 0x80, ' ', '(', 'M', ')', ' ', '=', + 'E', 'H', '4', 'M' | 0x80, ' ', '(', 'M', 'R', + '.', ')', ' ', '=', 'M', 'I', 'H', '4', + 'S', 'T', 'E', 'R' | 0x80, ' ', '(', 'M', 'S', + '.', ')', '=', 'M', 'I', 'H', '5', 'Z' | 0x80, + ' ', '(', 'M', 'R', 'S', '.', ')', ' ', + '=', 'M', 'I', 'H', '4', 'S', 'I', 'X', + 'Z' | 0x80, '(', 'M', 'O', 'V', ')', '=', 'M', + 'U', 'W', '4', 'V' | 0x80, '(', 'M', 'A', 'C', + 'H', 'I', 'N', ')', '=', 'M', 'A', 'H', + 'S', 'H', 'I', 'Y', '5', 'N' | 0x80, 'M', '(', + 'M', ')', '=' | 0x80, '(', 'M', ')', '=', 'M' | 0x80, + + ']', 'N' | 0x80, ' ', '(', 'N', ')', ' ', '=', + 'E', 'H', '4', 'N' | 0x80, 'E', '(', 'N', 'G', + ')', '+', '=', 'N', 'J' | 0x80, '(', 'N', 'G', + ')', 'R', '=', 'N', 'X', 'G' | 0x80, '(', 'N', + 'G', ')', '#', '=', 'N', 'X', 'G' | 0x80, '(', + 'N', 'G', 'L', ')', '%', '=', 'N', 'X', + 'G', 'U', 'L' | 0x80, '(', 'N', 'G', ')', '=', + 'N', 'X' | 0x80, '(', 'N', 'K', ')', '=', 'N', + 'X', 'K' | 0x80, ' ', '(', 'N', 'O', 'W', ')', + ' ', '=', 'N', 'A', 'W', '4' | 0x80, 'N', '(', + 'N', ')', '=' | 0x80, '(', 'N', 'O', 'N', ')', + 'E', '=', 'N', 'A', 'H', '4', 'N' | 0x80, '(', + 'N', ')', '=', 'N' | 0x80, + + ']', 'O' | 0x80, ' ', '(', 'O', ')', ' ', '=', + 'O', 'H', '4', 'W' | 0x80, '(', 'O', 'F', ')', + ' ', '=', 'A', 'H', 'V' | 0x80, ' ', '(', 'O', + 'H', ')', ' ', '=', 'O', 'W', '5' | 0x80, '(', + 'O', 'R', 'O', 'U', 'G', 'H', ')', '=', + 'E', 'R', '4', 'O', 'W' | 0x80, '#', ':', '(', + 'O', 'R', ')', ' ', '=', 'E', 'R' | 0x80, '#', + ':', '(', 'O', 'R', 'S', ')', ' ', '=', + 'E', 'R', 'Z' | 0x80, '(', 'O', 'R', ')', '=', + 'A', 'O', 'R' | 0x80, ' ', '(', 'O', 'N', 'E', + ')', '=', 'W', 'A', 'H', 'N' | 0x80, '#', '(', + 'O', 'N', 'E', ')', ' ', '=', 'W', 'A', + 'H', 'N' | 0x80, '(', 'O', 'W', ')', '=', 'O', + 'W' | 0x80, ' ', '(', 'O', 'V', 'E', 'R', ')', + '=', 'O', 'W', '5', 'V', 'E', 'R' | 0x80, 'P', + 'R', '(', 'O', ')', 'V', '=', 'U', 'W', + '4' | 0x80, '(', 'O', 'V', ')', '=', 'A', 'H', + '4', 'V' | 0x80, '(', 'O', ')', '^', '%', '=', + 'O', 'W', '5' | 0x80, '(', 'O', ')', '^', 'E', + 'N', '=', 'O', 'W' | 0x80, '(', 'O', ')', '^', + 'I', '#', '=', 'O', 'W', '5' | 0x80, '(', 'O', + 'L', ')', 'D', '=', 'O', 'W', '4', 'L' | 0x80, + '(', 'O', 'U', 'G', 'H', 'T', ')', '=', + 'A', 'O', '5', 'T' | 0x80, '(', 'O', 'U', 'G', + 'H', ')', '=', 'A', 'H', '5', 'F' | 0x80, ' ', + '(', 'O', 'U', ')', '=', 'A', 'W' | 0x80, 'H', + '(', 'O', 'U', ')', 'S', '#', '=', 'A', + 'W', '4' | 0x80, '(', 'O', 'U', 'S', ')', '=', + 'A', 'X', 'S' | 0x80, '(', 'O', 'U', 'R', ')', + '=', 'O', 'H', 'R' | 0x80, '(', 'O', 'U', 'L', + 'D', ')', '=', 'U', 'H', '5', 'D' | 0x80, '(', + 'O', 'U', ')', '^', 'L', '=', 'A', 'H', + '5' | 0x80, '(', 'O', 'U', 'P', ')', '=', 'U', + 'W', '5', 'P' | 0x80, '(', 'O', 'U', ')', '=', + 'A', 'W' | 0x80, '(', 'O', 'Y', ')', '=', 'O', + 'Y' | 0x80, '(', 'O', 'I', 'N', 'G', ')', '=', + 'O', 'W', '4', 'I', 'H', 'N', 'X' | 0x80, '(', + 'O', 'I', ')', '=', 'O', 'Y', '5' | 0x80, '(', + 'O', 'O', 'R', ')', '=', 'O', 'H', '5', + 'R' | 0x80, '(', 'O', 'O', 'K', ')', '=', 'U', + 'H', '5', 'K' | 0x80, 'F', '(', 'O', 'O', 'D', + ')', '=', 'U', 'W', '5', 'D' | 0x80, 'L', '(', + 'O', 'O', 'D', ')', '=', 'A', 'H', '5', + 'D' | 0x80, 'M', '(', 'O', 'O', 'D', ')', '=', + 'U', 'W', '5', 'D' | 0x80, '(', 'O', 'O', 'D', + ')', '=', 'U', 'H', '5', 'D' | 0x80, 'F', '(', + 'O', 'O', 'T', ')', '=', 'U', 'H', '5', + 'T' | 0x80, '(', 'O', 'O', ')', '=', 'U', 'W', + '5' | 0x80, '(', 'O', '\'', ')', '=', 'O', 'H' | 0x80, + '(', 'O', ')', 'E', '=', 'O', 'W' | 0x80, '(', + 'O', ')', ' ', '=', 'O', 'W' | 0x80, '(', 'O', + 'A', ')', '=', 'O', 'W', '4' | 0x80, ' ', '(', + 'O', 'N', 'L', 'Y', ')', '=', 'O', 'W', + '4', 'N', 'L', 'I', 'Y' | 0x80, ' ', '(', 'O', + 'N', 'C', 'E', ')', '=', 'W', 'A', 'H', + '4', 'N', 'S' | 0x80, '(', 'O', 'N', '\'', 'T', + ')', '=', 'O', 'W', '4', 'N', 'T' | 0x80, 'C', + '(', 'O', ')', 'N', '=', 'A', 'A' | 0x80, '(', + 'O', ')', 'N', 'G', '=', 'A', 'O' | 0x80, ' ', + ':', '^', '(', 'O', ')', 'N', '=', 'A', + 'H' | 0x80, 'I', '(', 'O', 'N', ')', '=', 'U', + 'N' | 0x80, '#', ':', '(', 'O', 'N', ')', '=', + 'U', 'N' | 0x80, '#', '^', '(', 'O', 'N', ')', + '=', 'U', 'N' | 0x80, '(', 'O', ')', 'S', 'T', + '=', 'O', 'W' | 0x80, '(', 'O', 'F', ')', '^', + '=', 'A', 'O', '4', 'F' | 0x80, '(', 'O', 'T', + 'H', 'E', 'R', ')', '=', 'A', 'H', '5', + 'D', 'H', 'E', 'R' | 0x80, 'R', '(', 'O', ')', + 'B', '=', 'R', 'A', 'A' | 0x80, '^', 'R', '(', + 'O', ')', ':', '#', '=', 'O', 'W', '5' | 0x80, + '(', 'O', 'S', 'S', ')', ' ', '=', 'A', + 'O', '5', 'S' | 0x80, '#', ':', '^', '(', 'O', + 'M', ')', '=', 'A', 'H', 'M' | 0x80, '(', 'O', + ')', '=', 'A', 'A' | 0x80, + + ']', 'P' | 0x80, ' ', '(', 'P', ')', ' ', '=', + 'P', 'I', 'Y', '4' | 0x80, '(', 'P', 'H', ')', + '=', 'F' | 0x80, '(', 'P', 'E', 'O', 'P', 'L', + ')', '=', 'P', 'I', 'Y', '5', 'P', 'U', + 'L' | 0x80, '(', 'P', 'O', 'W', ')', '=', 'P', + 'A', 'W', '4' | 0x80, '(', 'P', 'U', 'T', ')', + ' ', '=', 'P', 'U', 'H', 'T' | 0x80, '(', 'P', + ')', 'P', '=' | 0x80, '(', 'P', ')', 'S', '=' | 0x80, + '(', 'P', ')', 'N', '=' | 0x80, '(', 'P', 'R', + 'O', 'F', '.', ')', '=', 'P', 'R', 'O', + 'H', 'F', 'E', 'H', '4', 'S', 'E', 'R' | 0x80, + '(', 'P', ')', '=', 'P' | 0x80, + + ']', 'Q' | 0x80, ' ', '(', 'Q', ')', ' ', '=', + 'K', 'Y', 'U', 'W', '4' | 0x80, '(', 'Q', 'U', + 'A', 'R', ')', '=', 'K', 'W', 'O', 'H', + '5', 'R' | 0x80, '(', 'Q', 'U', ')', '=', 'K', + 'W' | 0x80, '(', 'Q', ')', '=', 'K' | 0x80, ']', 'R' | 0x80, + ' ', '(', 'R', ')', ' ', '=', 'A', 'A', + '5', 'R' | 0x80, ' ', '(', 'R', 'E', ')', '^', + '#', '=', 'R', 'I', 'Y' | 0x80, '(', 'R', ')', + 'R', '=' | 0x80, '(', 'R', ')', '=', 'R' | 0x80, + + ']', 'S' | 0x80, ' ', '(', 'S', ')', ' ', '=', + 'E', 'H', '4', 'S' | 0x80, '(', 'S', 'H', ')', + '=', 'S', 'H' | 0x80, '#', '(', 'S', 'I', 'O', + 'N', ')', '=', 'Z', 'H', 'U', 'N' | 0x80, '(', + 'S', 'O', 'M', 'E', ')', '=', 'S', 'A', + 'H', 'M' | 0x80, '#', '(', 'S', 'U', 'R', ')', + '#', '=', 'Z', 'H', 'E', 'R' | 0x80, '(', 'S', + 'U', 'R', ')', '#', '=', 'S', 'H', 'E', + 'R' | 0x80, '#', '(', 'S', 'U', ')', '#', '=', + 'Z', 'H', 'U', 'W' | 0x80, '#', '(', 'S', 'S', + 'U', ')', '#', '=', 'S', 'H', 'U', 'W' | 0x80, + '#', '(', 'S', 'E', 'D', ')', '=', 'Z', + 'D' | 0x80, '#', '(', 'S', ')', '#', '=', 'Z' | 0x80, + '(', 'S', 'A', 'I', 'D', ')', '=', 'S', + 'E', 'H', 'D' | 0x80, '^', '(', 'S', 'I', 'O', + 'N', ')', '=', 'S', 'H', 'U', 'N' | 0x80, '(', + 'S', ')', 'S', '=' | 0x80, '.', '(', 'S', ')', + ' ', '=', 'Z' | 0x80, '#', ':', '.', 'E', '(', + 'S', ')', ' ', '=', 'Z' | 0x80, '#', ':', '^', + '#', '(', 'S', ')', ' ', '=', 'S' | 0x80, 'U', + '(', 'S', ')', ' ', '=', 'S' | 0x80, ' ', ':', + '#', '(', 'S', ')', ' ', '=', 'Z' | 0x80, '#', + '#', '(', 'S', ')', ' ', '=', 'Z' | 0x80, ' ', + '(', 'S', 'C', 'H', ')', '=', 'S', 'K' | 0x80, + '(', 'S', ')', 'C', '+', '=' | 0x80, '#', '(', + 'S', 'M', ')', '=', 'Z', 'U', 'M' | 0x80, '#', + '(', 'S', 'N', ')', '\'', '=', 'Z', 'U', + 'M' | 0x80, '(', 'S', 'T', 'L', 'E', ')', '=', + 'S', 'U', 'L' | 0x80, '(', 'S', ')', '=', 'S' | 0x80, + + ']', 'T' | 0x80, ' ', '(', 'T', ')', ' ', '=', + 'T', 'I', 'Y', '4' | 0x80, ' ', '(', 'T', 'H', + 'E', ')', ' ', '#', '=', 'D', 'H', 'I', + 'Y' | 0x80, ' ', '(', 'T', 'H', 'E', ')', ' ', + '=', 'D', 'H', 'A', 'X' | 0x80, '(', 'T', 'O', + ')', ' ', '=', 'T', 'U', 'X' | 0x80, ' ', '(', + 'T', 'H', 'A', 'T', ')', '=', 'D', 'H', + 'A', 'E', 'T' | 0x80, ' ', '(', 'T', 'H', 'I', + 'S', ')', ' ', '=', 'D', 'H', 'I', 'H', + 'S' | 0x80, ' ', '(', 'T', 'H', 'E', 'Y', ')', + '=', 'D', 'H', 'E', 'Y' | 0x80, ' ', '(', 'T', + 'H', 'E', 'R', 'E', ')', '=', 'D', 'H', + 'E', 'H', 'R' | 0x80, '(', 'T', 'H', 'E', 'R', + ')', '=', 'D', 'H', 'E', 'R' | 0x80, '(', 'T', + 'H', 'E', 'I', 'R', ')', '=', 'D', 'H', + 'E', 'H', 'R' | 0x80, ' ', '(', 'T', 'H', 'A', + 'N', ')', ' ', '=', 'D', 'H', 'A', 'E', + 'N' | 0x80, ' ', '(', 'T', 'H', 'E', 'M', ')', + ' ', '=', 'D', 'H', 'A', 'E', 'N' | 0x80, '(', + 'T', 'H', 'E', 'S', 'E', ')', ' ', '=', + 'D', 'H', 'I', 'Y', 'Z' | 0x80, ' ', '(', 'T', + 'H', 'E', 'N', ')', '=', 'D', 'H', 'E', + 'H', 'N' | 0x80, '(', 'T', 'H', 'R', 'O', 'U', + 'G', 'H', ')', '=', 'T', 'H', 'R', 'U', + 'W', '4' | 0x80, '(', 'T', 'H', 'O', 'S', 'E', + ')', '=', 'D', 'H', 'O', 'H', 'Z' | 0x80, '(', + 'T', 'H', 'O', 'U', 'G', 'H', ')', ' ', + '=', 'D', 'H', 'O', 'W' | 0x80, '(', 'T', 'O', + 'D', 'A', 'Y', ')', '=', 'T', 'U', 'X', + 'D', 'E', 'Y' | 0x80, '(', 'T', 'O', 'M', 'O', + ')', 'R', 'R', 'O', 'W', '=', 'T', 'U', + 'M', 'A', 'A', '5' | 0x80, '(', 'T', 'O', ')', + 'T', 'A', 'L', '=', 'T', 'O', 'W', '5' | 0x80, + ' ', '(', 'T', 'H', 'U', 'S', ')', '=', + 'D', 'H', 'A', 'H', '4', 'S' | 0x80, '(', 'T', + 'H', ')', '=', 'T', 'H' | 0x80, '#', ':', '(', + 'T', 'E', 'D', ')', '=', 'T', 'I', 'X', + 'D' | 0x80, 'S', '(', 'T', 'I', ')', '#', 'N', + '=', 'C', 'H' | 0x80, '(', 'T', 'I', ')', 'O', + '=', 'S', 'H' | 0x80, '(', 'T', 'I', ')', 'A', + '=', 'S', 'H' | 0x80, '(', 'T', 'I', 'E', 'N', + ')', '=', 'S', 'H', 'U', 'N' | 0x80, '(', 'T', + 'U', 'R', ')', '#', '=', 'C', 'H', 'E', + 'R' | 0x80, '(', 'T', 'U', ')', 'A', '=', 'C', + 'H', 'U', 'W' | 0x80, ' ', '(', 'T', 'W', 'O', + ')', '=', 'T', 'U', 'W' | 0x80, '&', '(', 'T', + ')', 'E', 'N', ' ', '=' | 0x80, '(', 'T', ')', + '=', 'T' | 0x80, + + ']', 'U' | 0x80, ' ', '(', 'U', ')', ' ', '=', + 'Y', 'U', 'W', '4' | 0x80, ' ', '(', 'U', 'N', + ')', 'I', '=', 'Y', 'U', 'W', 'N' | 0x80, ' ', + '(', 'U', 'N', ')', '=', 'A', 'H', 'N' | 0x80, + ' ', '(', 'U', 'P', 'O', 'N', ')', '=', + 'A', 'X', 'P', 'A', 'O', 'N' | 0x80, '@', '(', + 'U', 'R', ')', '#', '=', 'U', 'H', '4', + 'R' | 0x80, '(', 'U', 'R', ')', '#', '=', 'Y', + 'U', 'H', '4', 'R' | 0x80, '(', 'U', 'R', ')', + '=', 'E', 'R' | 0x80, '(', 'U', ')', '^', ' ', + '=', 'A', 'H' | 0x80, '(', 'U', ')', '^', '^', + '=', 'A', 'H', '5' | 0x80, '(', 'U', 'Y', ')', + '=', 'A', 'Y', '5' | 0x80, ' ', 'G', '(', 'U', + ')', '#', '=' | 0x80, 'G', '(', 'U', ')', '%', + '=' | 0x80, 'G', '(', 'U', ')', '#', '=', 'W' | 0x80, + '#', 'N', '(', 'U', ')', '=', 'Y', 'U', + 'W' | 0x80, '@', '(', 'U', ')', '=', 'U', 'W' | 0x80, + '(', 'U', ')', '=', 'Y', 'U', 'W' | 0x80, + + ']', 'V' | 0x80, ' ', '(', 'V', ')', ' ', '=', + 'V', 'I', 'Y', '4' | 0x80, '(', 'V', 'I', 'E', + 'W', ')', '=', 'V', 'Y', 'U', 'W', '5' | 0x80, + '(', 'V', ')', '=', 'V' | 0x80, + + ']', 'W' | 0x80, ' ', '(', 'W', ')', ' ', '=', + 'D', 'A', 'H', '4', 'B', 'U', 'L', 'Y', + 'U', 'W' | 0x80, ' ', '(', 'W', 'E', 'R', 'E', + ')', '=', 'W', 'E', 'R' | 0x80, '(', 'W', 'A', + ')', 'S', 'H', '=', 'W', 'A', 'A' | 0x80, '(', + 'W', 'A', ')', 'S', 'T', '=', 'W', 'E', + 'Y' | 0x80, '(', 'W', 'A', ')', 'S', '=', 'W', + 'A', 'H' | 0x80, '(', 'W', 'A', ')', 'T', '=', + 'W', 'A', 'A' | 0x80, '(', 'W', 'H', 'E', 'R', + 'E', ')', '=', 'W', 'H', 'E', 'H', 'R' | 0x80, + '(', 'W', 'H', 'A', 'T', ')', '=', 'W', + 'H', 'A', 'H', 'T' | 0x80, '(', 'W', 'H', 'O', + 'L', ')', '=', '/', 'H', 'O', 'W', 'L' | 0x80, + '(', 'W', 'H', 'O', ')', '=', '/', 'H', + 'U', 'W' | 0x80, '(', 'W', 'H', ')', '=', 'W', + 'H' | 0x80, '(', 'W', 'A', 'R', ')', '#', '=', + 'W', 'E', 'H', 'R' | 0x80, '(', 'W', 'A', 'R', + ')', '=', 'W', 'A', 'O', 'R' | 0x80, '(', 'W', + 'O', 'R', ')', '^', '=', 'W', 'E', 'R' | 0x80, + '(', 'W', 'R', ')', '=', 'R' | 0x80, '(', 'W', + 'O', 'M', ')', 'A', '=', 'W', 'U', 'H', + 'M' | 0x80, '(', 'W', 'O', 'M', ')', 'E', '=', + 'W', 'I', 'H', 'M' | 0x80, '(', 'W', 'E', 'A', + ')', 'R', '=', 'W', 'E', 'H' | 0x80, '(', 'W', + 'A', 'N', 'T', ')', '=', 'W', 'A', 'A', + '5', 'N', 'T' | 0x80, 'A', 'N', 'S', '(', 'W', + 'E', 'R', ')', '=', 'E', 'R' | 0x80, '(', 'W', + ')', '=', 'W' | 0x80, + + ']', 'X' | 0x80, ' ', '(', 'X', ')', ' ', '=', + 'E', 'H', '4', 'K', 'R' | 0x80, ' ', '(', 'X', + ')', '=', 'Z' | 0x80, '(', 'X', ')', '=', 'K', + 'S' | 0x80, + + ']', 'Y' | 0x80, ' ', '(', 'Y', ')', ' ', '=', + 'W', 'A', 'Y', '4' | 0x80, '(', 'Y', 'O', 'U', + 'N', 'G', ')', '=', 'Y', 'A', 'H', 'N', + 'X' | 0x80, ' ', '(', 'Y', 'O', 'U', 'R', ')', + '=', 'Y', 'O', 'H', 'R' | 0x80, ' ', '(', 'Y', + 'O', 'U', ')', '=', 'Y', 'U', 'W' | 0x80, ' ', + '(', 'Y', 'E', 'S', ')', '=', 'Y', 'E', + 'H', 'S' | 0x80, ' ', '(', 'Y', ')', '=', 'Y' | 0x80, + 'F', '(', 'Y', ')', '=', 'A', 'Y' | 0x80, 'P', + 'S', '(', 'Y', 'C', 'H', ')', '=', 'A', + 'Y', 'K' | 0x80, '#', ':', '^', '(', 'Y', ')', + '=', 'I', 'Y' | 0x80, '#', ':', '^', '(', 'Y', + ')', 'I', '=', 'I', 'Y' | 0x80, ' ', ':', '(', + 'Y', ')', ' ', '=', 'A', 'Y' | 0x80, ' ', ':', + '(', 'Y', ')', '#', '=', 'A', 'Y' | 0x80, ' ', + ':', '(', 'Y', ')', '^', '+', ':', '#', + '=', 'I', 'H' | 0x80, ' ', ':', '(', 'Y', ')', + '^', '#', '=', 'A', 'Y' | 0x80, '(', 'Y', ')', + '=', 'I', 'H' | 0x80, + + ']', 'Z' | 0x80, ' ', '(', 'Z', ')', ' ', '=', + 'Z', 'I', 'Y', '4' | 0x80, '(', 'Z', ')', '=', + 'Z' | 0x80, 'j' | 0x80}; + +const unsigned char rules2[] = { + '(', 'A', ')', '=' | 0x80, '(', '!', ')', '=', + '.' | 0x80, '(', '"', ')', ' ', '=', '-', 'A', + 'H', '5', 'N', 'K', 'W', 'O', 'W', 'T', + '-' | 0x80, '(', '"', ')', '=', 'K', 'W', 'O', + 'W', '4', 'T', '-' | 0x80, '(', '#', ')', '=', + ' ', 'N', 'A', 'H', '4', 'M', 'B', 'E', + 'R' | 0x80, '(', '$', ')', '=', ' ', 'D', 'A', + 'A', '4', 'L', 'E', 'R' | 0x80, '(', '%', ')', + '=', ' ', 'P', 'E', 'R', 'S', 'E', 'H', + '4', 'N', 'T' | 0x80, '(', '&', ')', '=', ' ', + 'A', 'E', 'N', 'D' | 0x80, '(', '\'', ')', '=' | 0x80, + '(', '*', ')', '=', ' ', 'A', 'E', '4', + 'S', 'T', 'E', 'R', 'I', 'H', 'S', 'K' | 0x80, + '(', '+', ')', '=', ' ', 'P', 'L', 'A', + 'H', '4', 'S' | 0x80, '(', ',', ')', '=', ',' | 0x80, + ' ', '(', '-', ')', ' ', '=', '-' | 0x80, '(', + '-', ')', '=' | 0x80, '(', '.', ')', '=', ' ', + 'P', 'O', 'Y', 'N', 'T' | 0x80, '(', '/', ')', + '=', ' ', 'S', 'L', 'A', 'E', '4', 'S', + 'H' | 0x80, '(', '0', ')', '=', ' ', 'Z', 'I', + 'Y', '4', 'R', 'O', 'W' | 0x80, ' ', '(', '1', + 'S', 'T', ')', '=', 'F', 'E', 'R', '4', + 'S', 'T' | 0x80, ' ', '(', '1', '0', 'T', 'H', + ')', '=', 'T', 'E', 'H', '4', 'N', 'T', + 'H' | 0x80, '(', '1', ')', '=', ' ', 'W', 'A', + 'H', '4', 'N' | 0x80, ' ', '(', '2', 'N', 'D', + ')', '=', 'S', 'E', 'H', '4', 'K', 'U', + 'N', 'D' | 0x80, '(', '2', ')', '=', ' ', 'T', + 'U', 'W', '4' | 0x80, ' ', '(', '3', 'R', 'D', + ')', '=', 'T', 'H', 'E', 'R', '4', 'D' | 0x80, + '(', '3', ')', '=', ' ', 'T', 'H', 'R', + 'I', 'Y', '4' | 0x80, '(', '4', ')', '=', ' ', + 'F', 'O', 'H', '4', 'R' | 0x80, ' ', '(', '5', + 'T', 'H', ')', '=', 'F', 'I', 'H', '4', + 'F', 'T', 'H' | 0x80, '(', '5', ')', '=', ' ', + 'F', 'A', 'Y', '4', 'V' | 0x80, ' ', '(', '6', + '4', ')', ' ', '=', 'S', 'I', 'H', '4', + 'K', 'S', 'T', 'I', 'Y', ' ', 'F', 'O', + 'H', 'R' | 0x80, '(', '6', ')', '=', ' ', 'S', + 'I', 'H', '4', 'K', 'S' | 0x80, '(', '7', ')', + '=', ' ', 'S', 'E', 'H', '4', 'V', 'U', + 'N' | 0x80, ' ', '(', '8', 'T', 'H', ')', '=', + 'E', 'Y', '4', 'T', 'H' | 0x80, '(', '8', ')', + '=', ' ', 'E', 'Y', '4', 'T' | 0x80, '(', '9', + ')', '=', ' ', 'N', 'A', 'Y', '4', 'N' | 0x80, + '(', ':', ')', '=', '.' | 0x80, '(', ';', ')', + '=', '.' | 0x80, '(', '<', ')', '=', ' ', 'L', + 'E', 'H', '4', 'S', ' ', 'D', 'H', 'A', + 'E', 'N' | 0x80, '(', '=', ')', '=', ' ', 'I', + 'Y', '4', 'K', 'W', 'U', 'L', 'Z' | 0x80, '(', + '>', ')', '=', ' ', 'G', 'R', 'E', 'Y', + '4', 'T', 'E', 'R', ' ', 'D', 'H', 'A', + 'E', 'N' | 0x80, '(', '?', ')', '=', '?' | 0x80, '(', + '@', ')', '=', ' ', 'A', 'E', '6', 'T' | 0x80, + '(', '^', ')', '=', ' ', 'K', 'A', 'E', + '4', 'R', 'I', 'X', 'T' | 0x80, ']', 'A' | 0x80}; + +//26 items. From 'A' to 'Z' +// positions for mem62 and mem63 for each character +const unsigned char tab37489[] = {0, 149, 247, 162, 57, 197, 6, 126, 199, 38, 55, 78, 145, + 241, 85, 161, 254, 36, 69, 45, 167, 54, 83, 46, 71, 218}; + +const unsigned char tab37515[] = {125, 126, 126, 127, 128, 129, 130, 130, 130, 132, 132, 132, 132, + 132, 133, 135, 135, 136, 136, 137, 138, 139, 139, 140, 140, 140}; + +void STM32SAM::Output8BitAry(int index, unsigned char ary[5]) { + int k; + + uint32_t bufferposOld = bufferpos; + + bufferpos += timetable[oldtimetableindex][index]; + oldtimetableindex = index; + + int sample_uS = bufferpos - bufferposOld; + + uint32_t f = 0; + + // write a little bit in advance + for(k = 0; k < 5; k++) { + // buffer[bufferpos / 50 + k] = ary[k]; + + // f = micros() + sample_uS / (_STM32SAM_SPEED + 1); + // while(micros() < f) { + // }; + f = sample_uS / (_STM32SAM_SPEED + 1); + furi_delay_us(f); + SetAUDIO(ary[k]); + // delayMicroseconds(sample_uS / 5 ); + } + + // SetAUDIO(ary[0]); +} + +void STM32SAM::Output8Bit(int index, unsigned char A) { + unsigned char ary[5] = {A, A, A, A, A}; + Output8BitAry(index, ary); +} + +//written by me because of different table positions. +// mem[47] = ... +// 168=pitches +// 169=frequency1 +// 170=frequency2 +// 171=frequency3 +// 172=amplitude1 +// 173=amplitude2 +// 174=amplitude3 +unsigned char STM32SAM::Read(unsigned char p, unsigned char Y) { + switch(p) { + case 168: + return pitches[Y]; + case 169: + return frequency1[Y]; + case 170: + return frequency2[Y]; + case 171: + return frequency3[Y]; + case 172: + return amplitude1[Y]; + case 173: + return amplitude2[Y]; + case 174: + return amplitude3[Y]; + } + // Serial1.println("Error reading to tables"); + return 0; +} + +void STM32SAM::Write(unsigned char p, unsigned char Y, unsigned char value) { + switch(p) { + case 168: + pitches[Y] = value; + return; + case 169: + frequency1[Y] = value; + return; + case 170: + frequency2[Y] = value; + return; + case 171: + frequency3[Y] = value; + return; + case 172: + amplitude1[Y] = value; + return; + case 173: + amplitude2[Y] = value; + return; + case 174: + amplitude3[Y] = value; + return; + } + //Serial1.println("Error writing to tables\n"); +} + +// ------------------------------------------------------------------------- +//Code48227 +// Render a sampled sound from the sampleTable. +// +// Phoneme Sample Start Sample End +// 32: S* 15 255 +// 33: SH 257 511 +// 34: F* 559 767 +// 35: TH 583 767 +// 36: /H 903 1023 +// 37: /X 1135 1279 +// 38: Z* 84 119 +// 39: ZH 340 375 +// 40: V* 596 639 +// 41: DH 596 631 +// +// 42: CH +// 43: ** 399 511 +// +// 44: J* +// 45: ** 257 276 +// 46: ** +// +// 66: P* +// 67: ** 743 767 +// 68: ** +// +// 69: T* +// 70: ** 231 255 +// 71: ** +// +// The SampledPhonemesTable[] holds flags indicating if a phoneme is +// voiced or not. If the upper 5 bits are zero, the sample is voiced. +// +// Samples in the sampleTable are compressed, with bits being converted to +// bytes from high bit to low, as follows: +// +// unvoiced 0 bit -> X +// unvoiced 1 bit -> 5 +// +// voiced 0 bit -> 6 +// voiced 1 bit -> 24 +// +// Where X is a value from the table: +// +// { 0x18, 0x1A, 0x17, 0x17, 0x17 }; +// +// The index into this table is determined by masking off the lower +// 3 bits from the SampledPhonemesTable: +// +// index = (SampledPhonemesTable[i] & 7) - 1; +// +// For voices samples, samples are interleaved between voiced output. + +// Code48227() +void STM32SAM::RenderSample(unsigned char* mem66) { + int tempA; + // current phoneme's index + mem49 = Y; + + // mask low three bits and subtract 1 get value to + // convert 0 bits on unvoiced samples. + A = mem39 & 7; + X = A - 1; + + // store the result + mem56 = X; + + // determine which offset to use from table { 0x18, 0x1A, 0x17, 0x17, 0x17 } + // T, S, Z 0 0x18 + // CH, J, SH, ZH 1 0x1A + // P, F*, V, TH, DH 2 0x17 + // /H 3 0x17 + // /X 4 0x17 + + // get value from the table + mem53 = tab48426[X]; + mem47 = X; //46016+mem[56]*256 + + // voiced sample? + A = mem39 & 248; + if(A == 0) { + // voiced phoneme: Z*, ZH, V*, DH + Y = mem49; + A = pitches[mem49] >> 4; + + // jump to voiced portion + goto pos48315; + } + + Y = A ^ 255; +pos48274: + + // step through the 8 bits in the sample + mem56 = 8; + + // get the next sample from the table + // mem47*256 = offset to start of samples + A = sampleTable[mem47 * 256 + Y]; +pos48280: + + // left shift to get the high bit + tempA = A; + A = A << 1; + //48281: BCC 48290 + + // bit not set? + if((tempA & 128) == 0) { + // convert the bit to value from table + X = mem53; + //mem[54296] = X; + // output the byte + Output8Bit(1, (X & 0x0f) * 16); + // if X != 0, exit loop + if(X != 0) goto pos48296; + } + + // output a 5 for the on bit + Output8Bit(2, 5 * 16); + + //48295: NOP +pos48296: + + X = 0; + + // decrement counter + mem56--; + + // if not done, jump to top of loop + if(mem56 != 0) goto pos48280; + + // increment position + Y++; + if(Y != 0) goto pos48274; + + // restore values and return + mem44 = 1; + Y = mem49; + return; + + unsigned char phase1; + +pos48315: + // handle voiced samples here + + // number of samples? + phase1 = A ^ 255; + + Y = *mem66; + do { + //pos48321: + + // shift through all 8 bits + mem56 = 8; + //A = Read(mem47, Y); + + // fetch value from table + A = sampleTable[mem47 * 256 + Y]; + + // loop 8 times + //pos48327: + do { + //48327: ASL A + //48328: BCC 48337 + + // left shift and check high bit + tempA = A; + A = A << 1; + if((tempA & 128) != 0) { + // if bit set, output 26 + X = 26; + Output8Bit(3, (X & 0xf) * 16); + } else { + //timetable 4 + // bit is not set, output a 6 + X = 6; + Output8Bit(4, (X & 0xf) * 16); + } + + mem56--; + } while(mem56 != 0); + + // move ahead in the table + Y++; + + // continue until counter done + phase1++; + + } while(phase1 != 0); + // if (phase1 != 0) goto pos48321; + + // restore values and return + A = 1; + mem44 = 1; + *mem66 = Y; + Y = mem49; + return; +} + +// RENDER THE PHONEMES IN THE LIST +// +// The phoneme list is converted into sound through the steps: +// +// 1. Copy each phoneme number of times into the frames list, +// where each frame represents 10 milliseconds of sound. +// +// 2. Determine the transitions lengths between phonemes, and linearly +// interpolate the values across the frames. +// +// 3. Offset the pitches by the fundamental frequency. +// +// 4. Render the each frame. + +//void Code47574() +void STM32SAM::Render() { + unsigned char phase1 = 0; //mem43 + unsigned char phase2 = 0; + unsigned char phase3 = 0; + unsigned char mem66 = 0; + unsigned char mem38 = 0; + unsigned char mem40 = 0; + unsigned char speedcounter = 0; //mem45 + unsigned char mem48 = 0; + int i; + if(phonemeIndexOutput[0] == 255) return; //exit if no data + + A = 0; + X = 0; + mem44 = 0; + + // CREATE FRAMES + // + // The length parameter in the list corresponds to the number of frames + // to expand the phoneme to. Each frame represents 10 milliseconds of time. + // So a phoneme with a length of 7 = 7 frames = 70 milliseconds duration. + // + // The parameters are copied from the phoneme to the frame verbatim. + + // pos47587: + do { + // get the index + Y = mem44; + // get the phoneme at the index + A = phonemeIndexOutput[mem44]; + mem56 = A; + + // if terminal phoneme, exit the loop + if(A == 255) break; + + // period phoneme *. + if(A == 1) { + // add rising inflection + A = 1; + mem48 = 1; + //goto pos48376; + AddInflection(mem48, phase1); + } + /* + if (A == 2) goto pos48372; + */ + + // question mark phoneme? + if(A == 2) { + // create falling inflection + mem48 = 255; + AddInflection(mem48, phase1); + } + // pos47615: + + // get the stress amount (more stress = higher pitch) + phase1 = tab47492[stressOutput[Y] + 1]; + + // get number of frames to write + phase2 = phonemeLengthOutput[Y]; + Y = mem56; + + // copy from the source to the frames list + do { + frequency1[X] = freq1data[Y]; // F1 frequency + frequency2[X] = freq2data[Y]; // F2 frequency + frequency3[X] = freq3data[Y]; // F3 frequency + amplitude1[X] = ampl1data[Y]; // F1 amplitude + amplitude2[X] = ampl2data[Y]; // F2 amplitude + amplitude3[X] = ampl3data[Y]; // F3 amplitude + sampledConsonantFlag[X] = + sampledConsonantFlags[Y]; // phoneme data for sampled consonants + pitches[X] = pitch + phase1; // pitch + X++; + phase2--; + } while(phase2 != 0); + mem44++; + } while(mem44 != 0); + // ------------------- + //pos47694: + + // CREATE TRANSITIONS + // + // Linear transitions are now created to smoothly connect the + // end of one sustained portion of a phoneme to the following + // phoneme. + // + // To do this, three tables are used: + // + // Table Purpose + // ========= ================================================== + // blendRank Determines which phoneme's blend values are used. + // + // blendOut The number of frames at the end of the phoneme that + // will be used to transition to the following phoneme. + // + // blendIn The number of frames of the following phoneme that + // will be used to transition into that phoneme. + // + // In creating a transition between two phonemes, the phoneme + // with the HIGHEST rank is used. Phonemes are ranked on how much + // their identity is based on their transitions. For example, + // vowels are and diphthongs are identified by their sustained portion, + // rather than the transitions, so they are given low values. In contrast, + // stop consonants (P, B, T, K) and glides (Y, L) are almost entirely + // defined by their transitions, and are given high rank values. + // + // Here are the rankings used by SAM: + // + // Rank Type Phonemes + // 2 All vowels IY, IH, etc. + // 5 Diphthong endings YX, WX, ER + // 8 Terminal liquid consonants LX, WX, YX, N, NX + // 9 Liquid consonants L, RX, W + // 10 Glide R, OH + // 11 Glide WH + // 18 Voiceless fricatives S, SH, F, TH + // 20 Voiced fricatives Z, ZH, V, DH + // 23 Plosives, stop consonants P, T, K, KX, DX, CH + // 26 Stop consonants J, GX, B, D, G + // 27-29 Stop consonants (internal) ** + // 30 Unvoiced consonants /H, /X and Q* + // 160 Nasal M + // + // To determine how many frames to use, the two phonemes are + // compared using the blendRank[] table. The phoneme with the + // higher rank is selected. In case of a tie, a blend of each is used: + // + // if blendRank[phoneme1] == blendRank[phomneme2] + // // use lengths from each phoneme + // outBlendFrames = outBlend[phoneme1] + // inBlendFrames = outBlend[phoneme2] + // else if blendRank[phoneme1] > blendRank[phoneme2] + // // use lengths from first phoneme + // outBlendFrames = outBlendLength[phoneme1] + // inBlendFrames = inBlendLength[phoneme1] + // else + // // use lengths from the second phoneme + // // note that in and out are SWAPPED! + // outBlendFrames = inBlendLength[phoneme2] + // inBlendFrames = outBlendLength[phoneme2] + // + // Blend lengths can't be less than zero. + // + // Transitions are assumed to be symetrical, so if the transition + // values for the second phoneme are used, the inBlendLength and + // outBlendLength values are SWAPPED. + // + // For most of the parameters, SAM interpolates over the range of the last + // outBlendFrames-1 and the first inBlendFrames. + // + // The exception to this is the Pitch[] parameter, which is interpolates the + // pitch from the CENTER of the current phoneme to the CENTER of the next + // phoneme. + // + // Here are two examples. First, For example, consider the word "SUN" (S AH N) + // + // Phoneme Duration BlendWeight OutBlendFrames InBlendFrames + // S 2 18 1 3 + // AH 8 2 4 4 + // N 7 8 1 2 + // + // The formant transitions for the output frames are calculated as follows: + // + // flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch + // ------------------------------------------------ + // S + // 241 0 6 0 73 0 99 61 Use S (weight 18) for transition instead of AH (weight 2) + // 241 0 6 0 73 0 99 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames + // AH + // 0 2 10 2 66 0 96 59 * <-- InBlendFrames = 3 frames + // 0 4 14 3 59 0 93 57 * + // 0 8 18 5 52 0 90 55 * + // 0 15 22 9 44 1 87 53 + // 0 15 22 9 44 1 87 53 + // 0 15 22 9 44 1 87 53 Use N (weight 8) for transition instead of AH (weight 2). + // 0 15 22 9 44 1 87 53 Since N is second phoneme, reverse the IN and OUT values. + // 0 11 17 8 47 1 98 56 * <-- (InBlendFrames-1) = (2-1) = 1 frames + // N + // 0 8 12 6 50 1 109 58 * <-- OutBlendFrames = 1 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // + // Now, consider the reverse "NUS" (N AH S): + // + // flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch + // ------------------------------------------------ + // N + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 Use N (weight 8) for transition instead of AH (weight 2) + // 0 5 6 5 54 0 121 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames + // AH + // 0 8 11 6 51 0 110 59 * <-- InBlendFrames = 2 + // 0 11 16 8 48 0 99 56 * + // 0 15 22 9 44 1 87 53 Use S (weight 18) for transition instead of AH (weight 2) + // 0 15 22 9 44 1 87 53 Since S is second phoneme, reverse the IN and OUT values. + // 0 9 18 5 51 1 90 55 * <-- (InBlendFrames-1) = (3-1) = 2 + // 0 4 14 3 58 1 93 57 * + // S + // 241 2 10 2 65 1 96 59 * <-- OutBlendFrames = 1 + // 241 0 6 0 73 0 99 61 + + A = 0; + mem44 = 0; + mem49 = 0; // mem49 starts at as 0 + X = 0; + while(1) //while No. 1 + { + // get the current and following phoneme + Y = phonemeIndexOutput[X]; + A = phonemeIndexOutput[X + 1]; + X++; + + // exit loop at end token + if(A == 255) break; //goto pos47970; + + // get the ranking of each phoneme + X = A; + mem56 = blendRank[A]; + A = blendRank[Y]; + + // compare the rank - lower rank value is stronger + if(A == mem56) { + // same rank, so use out blend lengths from each phoneme + phase1 = outBlendLength[Y]; + phase2 = outBlendLength[X]; + } else if(A < mem56) { + // first phoneme is stronger, so us it's blend lengths + phase1 = inBlendLength[X]; + phase2 = outBlendLength[X]; + } else { + // second phoneme is stronger, so use it's blend lengths + // note the out/in are swapped + phase1 = outBlendLength[Y]; + phase2 = inBlendLength[Y]; + } + + Y = mem44; + A = mem49 + phonemeLengthOutput[mem44]; // A is mem49 + length + mem49 = A; // mem49 now holds length + position + A = A + phase2; //Maybe Problem because of carry flag + + //47776: ADC 42 + speedcounter = A; + mem47 = 168; + phase3 = mem49 - phase1; // what is mem49 + A = phase1 + phase2; // total transition? + mem38 = A; + + X = A; + X -= 2; + if((X & 128) == 0) + do //while No. 2 + { + //pos47810: + + // mem47 is used to index the tables: + // 168 pitches[] + // 169 frequency1 + // 170 frequency2 + // 171 frequency3 + // 172 amplitude1 + // 173 amplitude2 + // 174 amplitude3 + + mem40 = mem38; + + if(mem47 == 168) // pitch + { + // unlike the other values, the pitches[] interpolates from + // the middle of the current phoneme to the middle of the + // next phoneme + + unsigned char mem36, mem37; + // half the width of the current phoneme + mem36 = phonemeLengthOutput[mem44] >> 1; + // half the width of the next phoneme + mem37 = phonemeLengthOutput[mem44 + 1] >> 1; + // sum the values + mem40 = mem36 + mem37; // length of both halves + mem37 += mem49; // center of next phoneme + mem36 = mem49 - mem36; // center index of current phoneme + A = Read( + mem47, mem37); // value at center of next phoneme - end interpolation value + //A = mem[address]; + + Y = mem36; // start index of interpolation + mem53 = A - Read(mem47, mem36); // value to center of current phoneme + } else { + // value to interpolate to + A = Read(mem47, speedcounter); + // position to start interpolation from + Y = phase3; + // value to interpolate from + mem53 = A - Read(mem47, phase3); + } + + //Code47503(mem40); + // ML : Code47503 is division with remainder, and mem50 gets the sign + + // calculate change per frame + signed char m53 = (signed char)mem53; + mem50 = mem53 & 128; + unsigned char m53abs = abs(m53); + mem51 = m53abs % mem40; //abs((char)m53) % mem40; + mem53 = (unsigned char)((signed char)(m53) / mem40); + + // interpolation range + X = mem40; // number of frames to interpolate over + Y = phase3; // starting frame + + // linearly interpolate values + + mem56 = 0; + //47907: CLC + //pos47908: + while(1) //while No. 3 + { + A = Read(mem47, Y) + mem53; //carry alway cleared + + mem48 = A; + Y++; + X--; + if(X == 0) break; + + mem56 += mem51; + if(mem56 >= mem40) //??? + { + mem56 -= mem40; //carry? is set + //if ((mem56 & 128)==0) + if((mem50 & 128) == 0) { + //47935: BIT 50 + //47937: BMI 47943 + if(mem48 != 0) mem48++; + } else + mem48--; + } + //pos47945: + Write(mem47, Y, mem48); + } //while No. 3 + + //pos47952: + mem47++; + //if (mem47 != 175) goto pos47810; + } while(mem47 != 175); //while No. 2 + //pos47963: + mem44++; + X = mem44; + } //while No. 1 + + //goto pos47701; + //pos47970: + + // add the length of this phoneme + mem48 = mem49 + phonemeLengthOutput[mem44]; + + // ASSIGN PITCH CONTOUR + // + // This subtracts the F1 frequency from the pitch to create a + // pitch contour. Without this, the output would be at a single + // pitch level (monotone). + + // don't adjust pitch if in sing mode + if(!singmode) { + // iterate through the buffer + for(i = 0; i < 256; i++) { + // subtract half the frequency of the formant 1. + // this adds variety to the voice + pitches[i] -= (frequency1[i] >> 1); + } + } + + phase1 = 0; + phase2 = 0; + phase3 = 0; + mem49 = 0; + speedcounter = 72; //sam standard speed + + // RESCALE AMPLITUDE + // + // Rescale volume from a linear scale to decibels. + // + + //amplitude rescaling + for(i = 255; i >= 0; i--) { + amplitude1[i] = amplitudeRescale[amplitude1[i]]; + amplitude2[i] = amplitudeRescale[amplitude2[i]]; + amplitude3[i] = amplitudeRescale[amplitude3[i]]; + } + + Y = 0; + A = pitches[0]; + mem44 = A; + X = A; + mem38 = A - (A >> 2); // 3/4*A ??? + + // PROCESS THE FRAMES + // + // In traditional vocal synthesis, the glottal pulse drives filters, which + // are attenuated to the frequencies of the formants. + // + // SAM generates these formants directly with sin and rectangular waves. + // To simulate them being driven by the glottal pulse, the waveforms are + // reset at the beginning of each glottal pulse. + + //finally the loop for sound output + //pos48078: + while(1) { + // get the sampled information on the phoneme + A = sampledConsonantFlag[Y]; + mem39 = A; + + // unvoiced sampled phoneme? + A = A & 248; + if(A != 0) { + // render the sample for the phoneme + RenderSample(&mem66); + + // skip ahead two in the phoneme buffer + Y += 2; + mem48 -= 2; + } else { + // simulate the glottal pulse and formants + unsigned char ary[5]; + unsigned int p1 = + phase1 * 256; // Fixed point integers because we need to divide later on + unsigned int p2 = phase2 * 256; + unsigned int p3 = phase3 * 256; + int k; + for(k = 0; k < 5; k++) { + signed char sp1 = (signed char)sinus[0xff & (p1 >> 8)]; + signed char sp2 = (signed char)sinus[0xff & (p2 >> 8)]; + signed char rp3 = (signed char)rectangle[0xff & (p3 >> 8)]; + signed int sin1 = sp1 * ((unsigned char)amplitude1[Y] & 0x0f); + signed int sin2 = sp2 * ((unsigned char)amplitude2[Y] & 0x0f); + signed int rect = rp3 * ((unsigned char)amplitude3[Y] & 0x0f); + signed int mux = sin1 + sin2 + rect; + mux /= 32; + mux += 128; // Go from signed to unsigned amplitude + ary[k] = mux; + p1 += frequency1[Y] * 256 / 4; // Compromise, this becomes a shift and works well + p2 += frequency2[Y] * 256 / 4; + p3 += frequency3[Y] * 256 / 4; + } + // output the accumulated value + Output8BitAry(0, ary); + speedcounter--; + if(speedcounter != 0) goto pos48155; + Y++; //go to next amplitude + + // decrement the frame count + mem48--; + } + + // if the frame count is zero, exit the loop + if(mem48 == 0) return; + speedcounter = speed; + pos48155: + + // decrement the remaining length of the glottal pulse + mem44--; + + // finished with a glottal pulse? + if(mem44 == 0) { + pos48159: + // fetch the next glottal pulse length + A = pitches[Y]; + mem44 = A; + A = A - (A >> 2); + mem38 = A; + + // reset the formant wave generators to keep them in + // sync with the glottal pulse + phase1 = 0; + phase2 = 0; + phase3 = 0; + continue; + } + + // decrement the count + mem38--; + + // is the count non-zero and the sampled flag is zero? + if((mem38 != 0) || (mem39 == 0)) { + // reset the phase of the formants to match the pulse + phase1 += frequency1[Y]; + phase2 += frequency2[Y]; + phase3 += frequency3[Y]; + continue; + } + + // voiced sampled phonemes interleave the sample with the + // glottal pulse. The sample flag is non-zero, so render + // the sample for the phoneme. + RenderSample(&mem66); + goto pos48159; + } //while + + // The following code is never reached. It's left over from when + // the voiced sample code was part of this loop, instead of part + // of RenderSample(); + + //pos48315: + int tempA; + phase1 = A ^ 255; + Y = mem66; + do { + //pos48321: + + mem56 = 8; + A = Read(mem47, Y); + + //pos48327: + do { + //48327: ASL A + //48328: BCC 48337 + tempA = A; + A = A << 1; + if((tempA & 128) != 0) { + X = 26; + // mem[54296] = X; + bufferpos += 150; + // + // + // buffer[bufferpos / 50] = (X & 15) * 16; + // + // + + } else { + //mem[54296] = 6; + X = 6; + bufferpos += 150; + // + // buffer[bufferpos / 50] = (X & 15) * 16; + // + // + } + + for(X = wait2; X > 0; X--) + ; //wait + mem56--; + } while(mem56 != 0); + + Y++; + phase1++; + + } while(phase1 != 0); + // if (phase1 != 0) goto pos48321; + A = 1; + mem44 = 1; + mem66 = Y; + Y = mem49; + return; +} + +// Create a rising or falling inflection 30 frames prior to +// index X. A rising inflection is used for questions, and +// a falling inflection is used for statements. + +void STM32SAM::AddInflection(unsigned char mem48, unsigned char phase1) { + //pos48372: + // mem48 = 255; + //pos48376: + + // store the location of the punctuation + mem49 = X; + A = X; + int Atemp = A; + + // backup 30 frames + A = A - 30; + // if index is before buffer, point to start of buffer + if(Atemp <= 30) A = 0; + X = A; + + // FIXME: Explain this fix better, it's not obvious + // ML : A =, fixes a problem with invalid pitch with '.' + while((A = pitches[X]) == 127) X++; + +pos48398: + //48398: CLC + //48399: ADC 48 + + // add the inflection direction + A += mem48; + phase1 = A; + + // set the inflection + pitches[X] = A; +pos48406: + + // increment the position + X++; + + // exit if the punctuation has been reached + if(X == mem49) return; //goto pos47615; + if(pitches[X] == 255) goto pos48406; + A = phase1; + goto pos48398; +} + +/* + SAM's voice can be altered by changing the frequencies of the + mouth formant (F1) and the throat formant (F2). Only the voiced + phonemes (5-29 and 48-53) are altered. +*/ +void STM32SAM::SetMouthThroat() { + unsigned char initialFrequency; + unsigned char newFrequency = 0; + //unsigned char mouth; //mem38880 + //unsigned char throat; //mem38881 + + // mouth formants (F1) 5..29 + unsigned char mouthFormants5_29[30] = {0, 0, 0, 0, 0, 10, 14, 19, 24, 27, + 23, 21, 16, 20, 14, 18, 14, 18, 18, 16, + 13, 15, 11, 18, 14, 11, 9, 6, 6, 6}; + + // throat formants (F2) 5..29 + unsigned char throatFormants5_29[30] = {255, 255, 255, 255, 255, 84, 73, 67, 63, 40, + 44, 31, 37, 45, 73, 49, 36, 30, 51, 37, + 29, 69, 24, 50, 30, 24, 83, 46, 54, 86}; + + // there must be no zeros in this 2 tables + // formant 1 frequencies (mouth) 48..53 + unsigned char mouthFormants48_53[6] = {19, 27, 21, 27, 18, 13}; + + // formant 2 frequencies (throat) 48..53 + unsigned char throatFormants48_53[6] = {72, 39, 31, 43, 30, 34}; + + unsigned char pos = 5; //mem39216 + //pos38942: + // recalculate formant frequencies 5..29 for the mouth (F1) and throat (F2) + while(pos != 30) { + // recalculate mouth frequency + initialFrequency = mouthFormants5_29[pos]; + if(initialFrequency != 0) newFrequency = trans(mouth, initialFrequency); + freq1data[pos] = newFrequency; + + // recalculate throat frequency + initialFrequency = throatFormants5_29[pos]; + if(initialFrequency != 0) newFrequency = trans(throat, initialFrequency); + freq2data[pos] = newFrequency; + pos++; + } + + //pos39059: + // recalculate formant frequencies 48..53 + pos = 48; + Y = 0; + while(pos != 54) { + // recalculate F1 (mouth formant) + initialFrequency = mouthFormants48_53[Y]; + newFrequency = trans(mouth, initialFrequency); + freq1data[pos] = newFrequency; + + // recalculate F2 (throat formant) + initialFrequency = throatFormants48_53[Y]; + newFrequency = trans(throat, initialFrequency); + freq2data[pos] = newFrequency; + Y++; + pos++; + } +} + +//return = (mem39212*mem39213) >> 1 +unsigned char STM32SAM::trans(unsigned char mem39212, unsigned char mem39213) { + //pos39008: + unsigned char carry; + int temp; + unsigned char mem39214, mem39215; + A = 0; + mem39215 = 0; + mem39214 = 0; + X = 8; + do { + carry = mem39212 & 1; + mem39212 = mem39212 >> 1; + if(carry != 0) { + /* + 39018: LSR 39212 + 39021: BCC 39033 + */ + carry = 0; + A = mem39215; + temp = (int)A + (int)mem39213; + A = A + mem39213; + if(temp > 255) carry = 1; + mem39215 = A; + } + temp = mem39215 & 1; + mem39215 = (mem39215 >> 1) | (carry ? 128 : 0); + carry = temp; + //39033: ROR 39215 + X--; + } while(X != 0); + temp = mem39214 & 128; + mem39214 = (mem39214 << 1) | (carry ? 1 : 0); + carry = temp; + temp = mem39215 & 128; + mem39215 = (mem39215 << 1) | (carry ? 1 : 0); + carry = temp; + + return mem39215; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Sam +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//char input[]={"/HAALAOAO MAYN NAAMAEAE IHSTT SAEBAASTTIHAAN \x9b\x9b\0"}; +//unsigned char input[]={"/HAALAOAO \x9b\0"}; +//unsigned char input[]={"AA \x9b\0"}; +//unsigned char input[] = {"GUH5DEHN TAEG\x9b\0"}; + +//unsigned char input[]={"AY5 AEM EY TAO4LXKIHNX KAX4MPYUX4TAH. GOW4 AH/HEH3D PAHNK.MEYK MAY8 DEY.\x9b\0"}; +//unsigned char input[]={"/HEH3LOW2, /HAW AH YUX2 TUXDEY. AY /HOH3P YUX AH FIYLIHNX OW4 KEY.\x9b\0"}; +//unsigned char input[]={"/HEY2, DHIHS IH3Z GREY2T. /HAH /HAH /HAH.AYL BIY5 BAEK.\x9b\0"}; +//unsigned char input[]={"/HAH /HAH /HAH \x9b\0"}; +//unsigned char input[]={"/HAH /HAH /HAH.\x9b\0"}; +//unsigned char input[]={".TUW BIY5Y3,, OHR NAA3T - TUW BIY5IYIY., DHAE4T IHZ DHAH KWEH4SCHAHN.\x9b\0"}; +//unsigned char input[]={"/HEY2, DHIHS \x9b\0"}; + +//unsigned char input[]={" IYIHEHAEAAAHAOOHUHUXERAXIX \x9b\0"}; +//unsigned char input[]={" RLWWYMNNXBDGJZZHVDH \x9b\0"}; +//unsigned char input[]={" SSHFTHPTKCH/H \x9b\0"}; + +//unsigned char input[]={" EYAYOYAWOWUW ULUMUNQ YXWXRXLX/XDX\x9b\0"}; + +void STM32SAM::SetInput(char* _input) { + int i, l; + l = strlen(_input); + if(l > 254) l = 254; + for(i = 0; i < l; i++) { + input[i] = _input[i]; + } + input[l] = 0; +} + +// 168=pitches +// 169=frequency1 +// 170=frequency2 +// 171=frequency3 +// 172=amplitude1 +// 173=amplitude2 +// 174=amplitude3 + +void STM32SAM::Init() { + bufferpos = 0; + int i; + SetMouthThroat(); + + bufferpos = 0; + // TODO, check for free the memory, 10 seconds of output should be more than enough + //buffer = malloc(22050*10); + + // buffer = (char*) calloc(1, sizeof(char)); + + /* + freq2data = &mem[45136]; + freq1data = &mem[45056]; + freq3data = &mem[45216]; + */ + //pitches = &mem[43008]; + /* + frequency1 = &mem[43264]; + frequency2 = &mem[43520]; + frequency3 = &mem[43776]; + */ + /* + amplitude1 = &mem[44032]; + amplitude2 = &mem[44288]; + amplitude3 = &mem[44544]; + */ + //phoneme = &mem[39904]; + /* + ampl1data = &mem[45296]; + ampl2data = &mem[45376]; + ampl3data = &mem[45456]; + */ + + for(i = 0; i < 256; i++) { + stress[i] = 0; + phonemeLength[i] = 0; + } + + for(i = 0; i < 60; i++) { + phonemeIndexOutput[i] = 0; + stressOutput[i] = 0; + phonemeLengthOutput[i] = 0; + } + phonemeindex[255] = + 255; //to prevent buffer overflow // ML : changed from 32 to 255 to stop freezing with long inputs +} + +//int Code39771() +int STM32SAM::SAMMain() { + Init(); + phonemeindex[255] = 32; //to prevent buffer overflow + + if(!Parser1()) { + return 0; + } + + Parser2(); + CopyStress(); + SetPhonemeLength(); + AdjustLengths(); + Code41240(); + do { + A = phonemeindex[X]; + if(A > 80) { + phonemeindex[X] = 255; + break; // error: delete all behind it + } + X++; + } while(X != 0); + + //pos39848: + InsertBreath(); + + //mem[40158] = 255; + + PrepareOutput(); + + return 1; +} + +//void Code48547() +void STM32SAM::PrepareOutput() { + A = 0; + X = 0; + Y = 0; + + //pos48551: + while(1) { + A = phonemeindex[X]; + if(A == 255) { + A = 255; + phonemeIndexOutput[Y] = 255; + Render(); + return; + } + if(A == 254) { + X++; + int temp = X; + //mem[48546] = X; + phonemeIndexOutput[Y] = 255; + Render(); + //X = mem[48546]; + X = temp; + Y = 0; + continue; + } + + if(A == 0) { + X++; + continue; + } + + phonemeIndexOutput[Y] = A; + phonemeLengthOutput[Y] = phonemeLength[X]; + stressOutput[Y] = stress[X]; + X++; + Y++; + } +} + +//void Code41014() +void STM32SAM::Insert( + unsigned char position /*var57*/, + unsigned char mem60, + unsigned char mem59, + unsigned char mem58) { + int i; + for(i = 253; i >= position; i--) // ML : always keep last safe-guarding 255 + { + phonemeindex[i + 1] = phonemeindex[i]; + phonemeLength[i + 1] = phonemeLength[i]; + stress[i + 1] = stress[i]; + } + + phonemeindex[position] = mem60; + phonemeLength[position] = mem59; + stress[position] = mem58; + return; +} + +//void Code48431() +void STM32SAM::InsertBreath() { + unsigned char mem54; + unsigned char mem55; + unsigned char index; //variable Y + mem54 = 255; + X++; + mem55 = 0; + unsigned char mem66 = 0; + while(1) { + //pos48440: + X = mem66; + index = phonemeindex[X]; + if(index == 255) return; + mem55 += phonemeLength[X]; + + if(mem55 < 232) { + if(index != 254) // ML : Prevents an index out of bounds problem + { + A = flags2[index] & 1; + if(A != 0) { + X++; + mem55 = 0; + Insert(X, 254, mem59, 0); + mem66++; + mem66++; + continue; + } + } + if(index == 0) mem54 = X; + mem66++; + continue; + } + X = mem54; + phonemeindex[X] = 31; // 'Q*' glottal stop + phonemeLength[X] = 4; + stress[X] = 0; + X++; + mem55 = 0; + Insert(X, 254, mem59, 0); + X++; + mem66 = X; + } +} + +// Iterates through the phoneme buffer, copying the stress value from +// the following phoneme under the following circumstance: + +// 1. The current phoneme is voiced, excluding plosives and fricatives +// 2. The following phoneme is voiced, excluding plosives and fricatives, and +// 3. The following phoneme is stressed +// +// In those cases, the stress value+1 from the following phoneme is copied. +// +// For example, the word LOITER is represented as LOY5TER, with as stress +// of 5 on the diphtong OY. This routine will copy the stress value of 6 (5+1) +// to the L that precedes it. + +//void Code41883() +void STM32SAM::CopyStress() { + // loop thought all the phonemes to be output + unsigned char pos = 0; //mem66 + while(1) { + // get the phomene + Y = phonemeindex[pos]; + + // exit at end of buffer + if(Y == 255) return; + + // if CONSONANT_FLAG set, skip - only vowels get stress + if((flags[Y] & 64) == 0) { + pos++; + continue; + } + // get the next phoneme + Y = phonemeindex[pos + 1]; + if(Y == 255) //prevent buffer overflow + { + pos++; + continue; + } else + // if the following phoneme is a vowel, skip + if((flags[Y] & 128) == 0) { + pos++; + continue; + } + + // get the stress value at the next position + Y = stress[pos + 1]; + + // if next phoneme is not stressed, skip + if(Y == 0) { + pos++; + continue; + } + + // if next phoneme is not a VOWEL OR ER, skip + if((Y & 128) != 0) { + pos++; + continue; + } + + // copy stress from prior phoneme to this one + stress[pos] = Y + 1; + + // advance pointer + pos++; + } +} + +// The input[] buffer contains a string of phonemes and stress markers along +// the lines of: +// +// DHAX KAET IHZ AH5GLIY. <0x9B> +// +// The byte 0x9B marks the end of the buffer. Some phonemes are 2 bytes +// long, such as "DH" and "AX". Others are 1 byte long, such as "T" and "Z". +// There are also stress markers, such as "5" and ".". +// +// The first character of the phonemes are stored in the table signInputTable1[]. +// The second character of the phonemes are stored in the table signInputTable2[]. +// The stress characters are arranged in low to high stress order in stressInputTable[]. +// +// The following process is used to parse the input[] buffer: +// +// Repeat until the <0x9B> character is reached: +// +// First, a search is made for a 2 character match for phonemes that do not +// end with the '*' (wildcard) character. On a match, the index of the phoneme +// is added to phonemeIndex[] and the buffer position is advanced 2 bytes. +// +// If this fails, a search is made for a 1 character match against all +// phoneme names ending with a '*' (wildcard). If this succeeds, the +// phoneme is added to phonemeIndex[] and the buffer position is advanced +// 1 byte. +// +// If this fails, search for a 1 character match in the stressInputTable[]. +// If this succeeds, the stress value is placed in the last stress[] table +// at the same index of the last added phoneme, and the buffer position is +// advanced by 1 byte. +// +// If this fails, return a 0. +// +// On success: +// +// 1. phonemeIndex[] will contain the index of all the phonemes. +// 2. The last index in phonemeIndex[] will be 255. +// 3. stress[] will contain the stress value for each phoneme + +// input[] holds the string of phonemes, each two bytes wide +// signInputTable1[] holds the first character of each phoneme +// signInputTable2[] holds te second character of each phoneme +// phonemeIndex[] holds the indexes of the phonemes after parsing input[] +// +// The parser scans through the input[], finding the names of the phonemes +// by searching signInputTable1[] and signInputTable2[]. On a match, it +// copies the index of the phoneme into the phonemeIndexTable[]. +// +// The character <0x9B> marks the end of text in input[]. When it is reached, +// the index 255 is placed at the end of the phonemeIndexTable[], and the +// function returns with a 1 indicating success. +int STM32SAM::Parser1() { + int i; + unsigned char sign1; + unsigned char sign2; + unsigned char position = 0; + X = 0; + A = 0; + Y = 0; + + // CLEAR THE STRESS TABLE + for(i = 0; i < 256; i++) stress[i] = 0; + + // THIS CODE MATCHES THE PHONEME LETTERS TO THE TABLE + // pos41078: + while(1) { + // GET THE FIRST CHARACTER FROM THE PHONEME BUFFER + sign1 = input[X]; + // TEST FOR 155 (�) END OF LINE MARKER + if(sign1 == 155) { + // MARK ENDPOINT AND RETURN + phonemeindex[position] = 255; //mark endpoint + // REACHED END OF PHONEMES, SO EXIT + return 1; //all ok + } + + // GET THE NEXT CHARACTER FROM THE BUFFER + X++; + sign2 = input[X]; + + // NOW sign1 = FIRST CHARACTER OF PHONEME, AND sign2 = SECOND CHARACTER OF PHONEME + + // TRY TO MATCH PHONEMES ON TWO TWO-CHARACTER NAME + // IGNORE PHONEMES IN TABLE ENDING WITH WILDCARDS + + // SET INDEX TO 0 + Y = 0; + pos41095: + + // GET FIRST CHARACTER AT POSITION Y IN signInputTable + // --> should change name to PhonemeNameTable1 + A = signInputTable1[Y]; + + // FIRST CHARACTER MATCHES? + if(A == sign1) { + // GET THE CHARACTER FROM THE PhonemeSecondLetterTable + A = signInputTable2[Y]; + // NOT A SPECIAL AND MATCHES SECOND CHARACTER? + if((A != '*') && (A == sign2)) { + // STORE THE INDEX OF THE PHONEME INTO THE phomeneIndexTable + phonemeindex[position] = Y; + + // ADVANCE THE POINTER TO THE phonemeIndexTable + position++; + // ADVANCE THE POINTER TO THE phonemeInputBuffer + X++; + + // CONTINUE PARSING + continue; + } + } + + // NO MATCH, TRY TO MATCH ON FIRST CHARACTER TO WILDCARD NAMES (ENDING WITH '*') + + // ADVANCE TO THE NEXT POSITION + Y++; + // IF NOT END OF TABLE, CONTINUE + if(Y != 81) goto pos41095; + + // REACHED END OF TABLE WITHOUT AN EXACT (2 CHARACTER) MATCH. + // THIS TIME, SEARCH FOR A 1 CHARACTER MATCH AGAINST THE WILDCARDS + + // RESET THE INDEX TO POINT TO THE START OF THE PHONEME NAME TABLE + Y = 0; + pos41134: + // DOES THE PHONEME IN THE TABLE END WITH '*'? + if(signInputTable2[Y] == '*') { + // DOES THE FIRST CHARACTER MATCH THE FIRST LETTER OF THE PHONEME + if(signInputTable1[Y] == sign1) { + // SAVE THE POSITION AND MOVE AHEAD + phonemeindex[position] = Y; + + // ADVANCE THE POINTER + position++; + + // CONTINUE THROUGH THE LOOP + continue; + } + } + Y++; + if(Y != 81) goto pos41134; //81 is size of PHONEME NAME table + + // FAILED TO MATCH WITH A WILDCARD. ASSUME THIS IS A STRESS + // CHARACTER. SEARCH THROUGH THE STRESS TABLE + + // SET INDEX TO POSITION 8 (END OF STRESS TABLE) + Y = 8; + + // WALK BACK THROUGH TABLE LOOKING FOR A MATCH + while((sign1 != stressInputTable[Y]) && (Y > 0)) { + // DECREMENT INDEX + Y--; + } + + // REACHED THE END OF THE SEARCH WITHOUT BREAKING OUT OF LOOP? + if(Y == 0) { + //mem[39444] = X; + //41181: JSR 42043 //Error + // FAILED TO MATCH ANYTHING, RETURN 0 ON FAILURE + return 0; + } + // SET THE STRESS FOR THE PRIOR PHONEME + stress[position - 1] = Y; + } //while +} + +//change phonemelength depedendent on stress +//void Code41203() +void STM32SAM::SetPhonemeLength() { + unsigned char A; + int position = 0; + while(phonemeindex[position] != 255) { + A = stress[position]; + //41218: BMI 41229 + if((A == 0) || ((A & 128) != 0)) { + phonemeLength[position] = phonemeLengthTable[phonemeindex[position]]; + } else { + phonemeLength[position] = phonemeStressedLengthTable[phonemeindex[position]]; + } + position++; + } +} + +void STM32SAM::Code41240() { + unsigned char pos = 0; + + while(phonemeindex[pos] != 255) { + unsigned char index; //register AC + X = pos; + index = phonemeindex[pos]; + if((flags[index] & 2) == 0) { + pos++; + continue; + } else if((flags[index] & 1) == 0) { + Insert(pos + 1, index + 1, phonemeLengthTable[index + 1], stress[pos]); + Insert(pos + 2, index + 2, phonemeLengthTable[index + 2], stress[pos]); + pos += 3; + continue; + } + + do { + X++; + A = phonemeindex[X]; + } while(A == 0); + + if(A != 255) { + if((flags[A] & 8) != 0) { + pos++; + continue; + } + if((A == 36) || (A == 37)) { + pos++; // '/H' '/X' + continue; + } + } + + Insert(pos + 1, index + 1, phonemeLengthTable[index + 1], stress[pos]); + Insert(pos + 2, index + 2, phonemeLengthTable[index + 2], stress[pos]); + pos += 3; + }; +} + +// Rewrites the phonemes using the following rules: +// +// -> WX +// -> YX +// UL -> AX L +// UM -> AX M +// -> Q +// T R -> CH R +// D R -> J R +// R -> RX +// L -> LX +// G S -> G Z +// K -> KX +// G -> GX +// S P -> S B +// S T -> S D +// S K -> S G +// S KX -> S GX +// UW -> UX +// CH -> CH CH' (CH requires two phonemes to represent it) +// J -> J J' (J requires two phonemes to represent it) +// T -> DX +// D -> DX + +//void Code41397() +void STM32SAM::Parser2() { + unsigned char pos = 0; //mem66; + unsigned char mem58 = 0; + + // Loop through phonemes + while(1) { + // SET X TO THE CURRENT POSITION + X = pos; + // GET THE PHONEME AT THE CURRENT POSITION + A = phonemeindex[pos]; + + // Is phoneme pause? + if(A == 0) { + // Move ahead to the + pos++; + continue; + } + + // If end of phonemes flag reached, exit routine + if(A == 255) return; + + // Copy the current phoneme index to Y + Y = A; + + // RULE: + // -> WX + // -> YX + // Example: OIL, COW + + // Check for DIPHTONG + if((flags[A] & 16) == 0) goto pos41457; + + // Not a diphthong. Get the stress + mem58 = stress[pos]; + + // End in IY sound? + A = flags[Y] & 32; + + // If ends with IY, use YX, else use WX + if(A == 0) + A = 20; + else + A = 21; // 'WX' = 20 'YX' = 21 + //pos41443: + // Insert at WX or YX following, copying the stress + + Insert(pos + 1, A, mem59, mem58); + X = pos; + // Jump to ??? + goto pos41749; + + pos41457: + + // RULE: + // UL -> AX L + // Example: MEDDLE + + // Get phoneme + A = phonemeindex[X]; + // Skip this rule if phoneme is not UL + if(A != 78) goto pos41487; // 'UL' + A = 24; // 'L' //change 'UL' to 'AX L' + + pos41466: + // Get current phoneme stress + mem58 = stress[X]; + + // Change UL to AX + phonemeindex[X] = 13; // 'AX' + // Perform insert. Note code below may jump up here with different values + Insert(X + 1, A, mem59, mem58); + pos++; + // Move to next phoneme + continue; + + pos41487: + + // RULE: + // UM -> AX M + // Example: ASTRONOMY + + // Skip rule if phoneme != UM + if(A != 79) goto pos41495; // 'UM' + // Jump up to branch - replaces current phoneme with AX and continues + A = 27; // 'M' //change 'UM' to 'AX M' + + goto pos41466; + pos41495: + + // RULE: + // UN -> AX N + // Example: FUNCTION + + // Skip rule if phoneme != UN + if(A != 80) goto pos41503; // 'UN' + + // Jump up to branch - replaces current phoneme with AX and continues + A = 28; // 'N' //change UN to 'AX N' + + goto pos41466; + pos41503: + + // RULE: + // -> Q + // EXAMPLE: AWAY EIGHT + + Y = A; + // VOWEL set? + A = flags[A] & 128; + + // Skip if not a vowel + if(A != 0) { + // Get the stress + A = stress[X]; + + // If stressed... + if(A != 0) { + // Get the following phoneme + X++; + A = phonemeindex[X]; + // If following phoneme is a pause + + if(A == 0) { + // Get the phoneme following pause + X++; + Y = phonemeindex[X]; + + // Check for end of buffer flag + if(Y == 255) //buffer overflow + // ??? Not sure about these flags + A = 65 & 128; + else + // And VOWEL flag to current phoneme's flags + A = flags[Y] & 128; + + // If following phonemes is not a pause + if(A != 0) { + // If the following phoneme is not stressed + A = stress[X]; + if(A != 0) { + // 31 = 'Q' + Insert(X, 31, mem59, 0); + pos++; + continue; + } + } + } + } + } + + // RULES FOR PHONEMES BEFORE R + // T R -> CH R + // Example: TRACK + + // Get current position and phoneme + X = pos; + A = phonemeindex[pos]; + if(A != 23) goto pos41611; // 'R' + + // Look at prior phoneme + X--; + A = phonemeindex[pos - 1]; + //pos41567: + if(A == 69) // 'T' + { + phonemeindex[pos - 1] = 42; + goto pos41779; + } + + // RULES FOR PHONEMES BEFORE R + // D R -> J R + // Example: DRY + + // Prior phonemes D? + if(A == 57) // 'D' + { + // Change D to J + phonemeindex[pos - 1] = 44; + + goto pos41788; + } + + // RULES FOR PHONEMES BEFORE R + // R -> RX + // Example: ART + + // If vowel flag is set change R to RX + A = flags[A] & 128; + + if(A != 0) phonemeindex[pos] = 18; // 'RX' + + // continue to next phoneme + pos++; + continue; + + pos41611: + + // RULE: + // L -> LX + // Example: ALL + + // Is phoneme L? + if(A == 24) // 'L' + { + // If prior phoneme does not have VOWEL flag set, move to next phoneme + if((flags[phonemeindex[pos - 1]] & 128) == 0) { + pos++; + continue; + } + // Prior phoneme has VOWEL flag set, so change L to LX and move to next phoneme + + phonemeindex[X] = 19; // 'LX' + pos++; + continue; + } + + // RULE: + // G S -> G Z + // + // Can't get to fire - + // 1. The G -> GX rule intervenes + // 2. Reciter already replaces GS -> GZ + + // Is current phoneme S? + if(A == 32) // 'S' + { + // If prior phoneme is not G, move to next phoneme + if(phonemeindex[pos - 1] != 60) { + pos++; + continue; + } + // Replace S with Z and move on + + phonemeindex[pos] = 38; // 'Z' + pos++; + continue; + } + + // RULE: + // K -> KX + // Example: COW + + // Is current phoneme K? + if(A == 72) // 'K' + { + // Get next phoneme + Y = phonemeindex[pos + 1]; + // If at end, replace current phoneme with KX + if(Y == 255) + phonemeindex[pos] = 75; // ML : prevents an index out of bounds problem + else { + // VOWELS AND DIPHTONGS ENDING WITH IY SOUND flag set? + A = flags[Y] & 32; + + // Replace with KX + if(A == 0) phonemeindex[pos] = 75; // 'KX' + } + } else + + // RULE: + // G -> GX + // Example: GO + + // Is character a G? + if(A == 60) // 'G' + { + // Get the following character + unsigned char index = phonemeindex[pos + 1]; + + // At end of buffer? + if(index == 255) //prevent buffer overflow + { + pos++; + continue; + } else + // If diphtong ending with YX, move continue processing next phoneme + if((flags[index] & 32) != 0) { + pos++; + continue; + } + // replace G with GX and continue processing next phoneme + + phonemeindex[pos] = 63; // 'GX' + pos++; + continue; + } + + // RULE: + // S P -> S B + // S T -> S D + // S K -> S G + // S KX -> S GX + // Examples: SPY, STY, SKY, SCOWL + + Y = phonemeindex[pos]; + //pos41719: + // Replace with softer version? + A = flags[Y] & 1; + if(A == 0) goto pos41749; + A = phonemeindex[pos - 1]; + if(A != 32) // 'S' + { + A = Y; + goto pos41812; + } + // Replace with softer version + + phonemeindex[pos] = Y - 12; + pos++; + continue; + + pos41749: + + // RULE: + // UW -> UX + // + // Example: NEW, DEW, SUE, ZOO, THOO, TOO + + // UW -> UX + + A = phonemeindex[X]; + if(A == 53) // 'UW' + { + // ALVEOLAR flag set? + Y = phonemeindex[X - 1]; + A = flags2[Y] & 4; + // If not set, continue processing next phoneme + if(A == 0) { + pos++; + continue; + } + + phonemeindex[X] = 16; + pos++; + continue; + } + pos41779: + + // RULE: + // CH -> CH CH' (CH requires two phonemes to represent it) + // Example: CHEW + + if(A == 42) // 'CH' + { + // pos41783: + + Insert(X + 1, A + 1, mem59, stress[X]); + pos++; + continue; + } + + pos41788: + + // RULE: + // J -> J J' (J requires two phonemes to represent it) + // Example: JAY + + if(A == 44) // 'J' + { + Insert(X + 1, A + 1, mem59, stress[X]); + pos++; + continue; + } + + // Jump here to continue + pos41812: + + // RULE: Soften T following vowel + // NOTE: This rule fails for cases such as "ODD" + // T -> DX + // D -> DX + // Example: PARTY, TARDY + + // Past this point, only process if phoneme is T or D + + if(A != 69) // 'T' + if(A != 57) { + pos++; // 'D' + continue; + } + //pos41825: + + // If prior phoneme is not a vowel, continue processing phonemes + if((flags[phonemeindex[X - 1]] & 128) == 0) { + pos++; + continue; + } + + // Get next phoneme + X++; + A = phonemeindex[X]; + //pos41841 + // Is the next phoneme a pause? + if(A != 0) { + // If next phoneme is not a pause, continue processing phonemes + if((flags[A] & 128) == 0) { + pos++; + continue; + } + // If next phoneme is stressed, continue processing phonemes + // FIXME: How does a pause get stressed? + if(stress[X] != 0) { + pos++; + continue; + } + //pos41856: + // Set phonemes to DX + + phonemeindex[pos] = 30; // 'DX' + } else { + A = phonemeindex[X + 1]; + if(A == 255) //prevent buffer overflow + A = 65 & 128; + else + // Is next phoneme a vowel or ER? + A = flags[A] & 128; + + if(A != 0) phonemeindex[pos] = 30; // 'DX' + } + + pos++; + + } // while +} // parser 2 + +// Applies various rules that adjust the lengths of phonemes +// +// Lengthen or between and by 1.5 +// - decrease length by 1 +// - decrease vowel by 1/8th +// - increase vowel by 1/2 + 1 +// - set nasal = 5, consonant = 6 +// {optional silence} - shorten both to 1/2 + 1 +// - decrease by 2 + +//void Code48619() +void STM32SAM::AdjustLengths() { + // LENGTHEN VOWELS PRECEDING PUNCTUATION + // + // Search for punctuation. If found, back up to the first vowel, then + // process all phonemes between there and up to (but not including) the punctuation. + // If any phoneme is found that is a either a fricative or voiced, the duration is + // increased by (length * 1.5) + 1 + + // loop index + X = 0; + unsigned char index; + + // iterate through the phoneme list + unsigned char loopIndex = 0; + while(1) { + // get a phoneme + index = phonemeindex[X]; + + // exit loop if end on buffer token + if(index == 255) break; + + // not punctuation? + if((flags2[index] & 1) == 0) { + // skip + X++; + continue; + } + + // hold index + loopIndex = X; + + // Loop backwards from this point + pos48644: + + // back up one phoneme + X--; + + // stop once the beginning is reached + if(X == 0) break; + + // get the preceding phoneme + index = phonemeindex[X]; + + if(index != 255) //inserted to prevent access overrun + if((flags[index] & 128) == 0) goto pos48644; // if not a vowel, continue looping + + //pos48657: + do { + // test for vowel + index = phonemeindex[X]; + + if(index != 255) //inserted to prevent access overrun + // test for fricative/unvoiced or not voiced + if(((flags2[index] & 32) == 0) || ((flags[index] & 4) != 0)) //nochmal �berpr�fen + { + //A = flags[Y] & 4; + //if(A == 0) goto pos48688; + + // get the phoneme length + A = phonemeLength[X]; + + // change phoneme length to (length * 1.5) + 1 + A = (A >> 1) + A + 1; + + phonemeLength[X] = A; + } + // keep moving forward + X++; + } while(X != loopIndex); + // if (X != loopIndex) goto pos48657; + X++; + } // while + + // Similar to the above routine, but shorten vowels under some circumstances + + // Loop throught all phonemes + loopIndex = 0; + //pos48697 + + while(1) { + // get a phoneme + X = loopIndex; + index = phonemeindex[X]; + + // exit routine at end token + if(index == 255) return; + + // vowel? + A = flags[index] & 128; + if(A != 0) { + // get next phoneme + X++; + index = phonemeindex[X]; + + // get flags + if(index == 255) + mem56 = 65; // use if end marker + else + mem56 = flags[index]; + + // not a consonant + if((flags[index] & 64) == 0) { + // RX or LX? + if((index == 18) || (index == 19)) // 'RX' & 'LX' + { + // get the next phoneme + X++; + index = phonemeindex[X]; + + // next phoneme a consonant? + if((flags[index] & 64) != 0) { + // RULE: RX | LX + + // decrease length of vowel by 1 frame + phonemeLength[loopIndex]--; + } + // move ahead + loopIndex++; + continue; + } + // move ahead + loopIndex++; + continue; + } + + // Got here if not + + // not voiced + if((mem56 & 4) == 0) { + // Unvoiced + // *, .*, ?*, ,*, -*, DX, S*, SH, F*, TH, /H, /X, CH, P*, T*, K*, KX + + // not an unvoiced plosive? + if((mem56 & 1) == 0) { + // move ahead + loopIndex++; + continue; + } + + // P*, T*, K*, KX + + // RULE: + // + + // move back + X--; + + // decrease length by 1/8th + mem56 = phonemeLength[X] >> 3; + phonemeLength[X] -= mem56; + + // move ahead + loopIndex++; + continue; + } + + // RULE: + // + + // decrease length + A = phonemeLength[X - 1]; + phonemeLength[X - 1] = (A >> 2) + A + 1; // 5/4*A + 1 + + // move ahead + loopIndex++; + continue; + } + + // WH, R*, L*, W*, Y*, M*, N*, NX, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX + + //pos48821: + + // RULE: + // Set punctuation length to 6 + // Set stop consonant length to 5 + + // nasal? + if((flags2[index] & 8) != 0) { + // M*, N*, NX, + + // get the next phoneme + X++; + index = phonemeindex[X]; + + // end of buffer? + if(index == 255) + A = 65 & 2; //prevent buffer overflow + else + A = flags[index] & 2; // check for stop consonant + + // is next phoneme a stop consonant? + if(A != 0) + + // B*, D*, G*, GX, P*, T*, K*, KX + + { + // set stop consonant length to 6 + phonemeLength[X] = 6; + + // set nasal length to 5 + phonemeLength[X - 1] = 5; + } + // move to next phoneme + loopIndex++; + continue; + } + + // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX + + // RULE: {optional silence} + // Shorten both to (length/2 + 1) + + // (voiced) stop consonant? + if((flags[index] & 2) != 0) { + // B*, D*, G*, GX + + // move past silence + do { + // move ahead + X++; + index = phonemeindex[X]; + } while(index == 0); + + // check for end of buffer + if(index == 255) //buffer overflow + { + // ignore, overflow code + if((65 & 2) == 0) { + loopIndex++; + continue; + } + } else if((flags[index] & 2) == 0) { + // if another stop consonant, move ahead + loopIndex++; + continue; + } + + // RULE: {optional silence} + + // X gets overwritten, so hold prior X value for debug statement + // int debugX = X; + // shorten the prior phoneme length to (length/2 + 1) + phonemeLength[X] = (phonemeLength[X] >> 1) + 1; + X = loopIndex; + + // also shorten this phoneme length to (length/2 +1) + phonemeLength[loopIndex] = (phonemeLength[loopIndex] >> 1) + 1; + + // move ahead + loopIndex++; + continue; + } + + // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, **, + + // RULE: + // Decrease by 2 + + // liquic consonant? + if((flags2[index] & 16) != 0) { + // R*, L*, W*, Y* + + // get the prior phoneme + index = phonemeindex[X - 1]; + + // prior phoneme a stop consonant> + if((flags[index] & 2) != 0) { + // Rule: + + // decrease the phoneme length by 2 frames (20 ms) + phonemeLength[X] -= 2; + } + } + + // move to next phoneme + loopIndex++; + continue; + } + // goto pos48701; +} + +// ------------------------------------------------------------------------- +// ML : Code47503 is division with remainder, and mem50 gets the sign +void STM32SAM::Code47503(unsigned char mem52) { + Y = 0; + if((mem53 & 128) != 0) { + mem53 = -mem53; + Y = 128; + } + mem50 = Y; + A = 0; + for(X = 8; X > 0; X--) { + int temp = mem53; + mem53 = mem53 << 1; + A = A << 1; + if(temp >= 128) A++; + if(A >= mem52) { + A = A - mem52; + mem53++; + } + } + + mem51 = A; + if((mem50 & 128) != 0) mem53 = -mem53; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Reciter +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::Code37055(unsigned char mem59) { + X = mem59; + X--; + A = inputtemp[X]; + Y = A; + A = tab36376[Y]; + return; +} + +void STM32SAM::Code37066(unsigned char mem58) { + X = mem58; + X++; + A = inputtemp[X]; + Y = A; + A = tab36376[Y]; +} + +unsigned char STM32SAM::GetRuleByte(unsigned short mem62, unsigned char Y) { + unsigned int address = mem62; + + if(mem62 >= 37541) { + address -= 37541; + return rules2[address + Y]; + } + address -= 32000; + return rules[address + Y]; +} + +int STM32SAM::TextToPhonemes(unsigned char* input) // Code36484 +{ + //unsigned char *tab39445 = &mem[39445]; //input and output + //unsigned char mem29; + unsigned char mem56; //output position for phonemes + unsigned char mem57; + unsigned char mem58; + unsigned char mem59; + unsigned char mem60; + unsigned char mem61; + unsigned short mem62; // memory position of current rule + + unsigned char mem64; // position of '=' or current character + unsigned char mem65; // position of ')' + unsigned char mem66; // position of '(' + unsigned char mem36653; + + inputtemp[0] = 32; + + // secure copy of input + // because input will be overwritten by phonemes + X = 1; + Y = 0; + do { + //pos36499: + A = input[Y] & 127; + if(A >= 112) + A = A & 95; + else if(A >= 96) + A = A & 79; + + inputtemp[X] = A; + X++; + Y++; + } while(Y != 255); + + X = 255; + inputtemp[X] = 27; + mem61 = 255; + +pos36550: + A = 255; + mem56 = 255; + +pos36554: + while(1) { + mem61++; + X = mem61; + A = inputtemp[X]; + mem64 = A; + if(A == '[') { + mem56++; + X = mem56; + A = 155; + input[X] = 155; + //goto pos36542; + // Code39771(); //Code39777(); + return 1; + } + + //pos36579: + if(A != '.') break; + X++; + Y = inputtemp[X]; + A = tab36376[Y] & 1; + if(A != 0) break; + mem56++; + X = mem56; + A = '.'; + input[X] = '.'; + } //while + + //pos36607: + A = mem64; + Y = A; + A = tab36376[A]; + mem57 = A; + if((A & 2) != 0) { + mem62 = 37541; + goto pos36700; + } + + //pos36630: + A = mem57; + if(A != 0) goto pos36677; + A = 32; + inputtemp[X] = ' '; + mem56++; + X = mem56; + if(X > 120) goto pos36654; + input[X] = A; + goto pos36554; + + // ----- + + //36653 is unknown. Contains position + +pos36654: + input[X] = 155; + A = mem61; + mem36653 = A; + // mem29 = A; // not used + // Code36538(); das ist eigentlich + return 1; + //Code39771(); + //go on if there is more input ??? + mem61 = mem36653; + goto pos36550; + +pos36677: + A = mem57 & 128; + if(A == 0) { + //36683: BRK + return 0; + } + + // go to the right rules for this character. + X = mem64 - 'A'; + mem62 = tab37489[X] | (tab37515[X] << 8); + + // ------------------------------------- + // go to next rule + // ------------------------------------- + +pos36700: + + // find next rule + Y = 0; + do { + mem62 += 1; + A = GetRuleByte(mem62, Y); + } while((A & 128) == 0); + Y++; + + //pos36720: + // find '(' + while(1) { + A = GetRuleByte(mem62, Y); + if(A == '(') break; + Y++; + } + mem66 = Y; + + //pos36732: + // find ')' + do { + Y++; + A = GetRuleByte(mem62, Y); + } while(A != ')'); + mem65 = Y; + + //pos36741: + // find '=' + do { + Y++; + A = GetRuleByte(mem62, Y); + A = A & 127; + } while(A != '='); + mem64 = Y; + + X = mem61; + mem60 = X; + + // compare the string within the bracket + Y = mem66; + Y++; + //pos36759: + while(1) { + mem57 = inputtemp[X]; + A = GetRuleByte(mem62, Y); + if(A != mem57) goto pos36700; + Y++; + if(Y == mem65) break; + X++; + mem60 = X; + } + + // the string in the bracket is correct + + //pos36787: + A = mem61; + mem59 = mem61; + +pos36791: + while(1) { + mem66--; + Y = mem66; + A = GetRuleByte(mem62, Y); + mem57 = A; + //36800: BPL 36805 + if((A & 128) != 0) goto pos37180; + X = A & 127; + A = tab36376[X] & 128; + if(A == 0) break; + X = mem59 - 1; + A = inputtemp[X]; + if(A != mem57) goto pos36700; + mem59 = X; + } + + //pos36833: + A = mem57; + if(A == ' ') goto pos36895; + if(A == '#') goto pos36910; + if(A == '.') goto pos36920; + if(A == '&') goto pos36935; + if(A == '@') goto pos36967; + if(A == '^') goto pos37004; + if(A == '+') goto pos37019; + if(A == ':') goto pos37040; + // Code42041(); //Error + //36894: BRK + return 0; + + // -------------- + +pos36895: + Code37055(mem59); + A = A & 128; + if(A != 0) goto pos36700; +pos36905: + mem59 = X; + goto pos36791; + + // -------------- + +pos36910: + Code37055(mem59); + A = A & 64; + if(A != 0) goto pos36905; + goto pos36700; + + // -------------- + +pos36920: + Code37055(mem59); + A = A & 8; + if(A == 0) goto pos36700; +pos36930: + mem59 = X; + goto pos36791; + + // -------------- + +pos36935: + Code37055(mem59); + A = A & 16; + if(A != 0) goto pos36930; + A = inputtemp[X]; + if(A != 72) goto pos36700; + X--; + A = inputtemp[X]; + if((A == 67) || (A == 83)) goto pos36930; + goto pos36700; + + // -------------- + +pos36967: + Code37055(mem59); + A = A & 4; + if(A != 0) goto pos36930; + A = inputtemp[X]; + if(A != 72) goto pos36700; + if((A != 84) && (A != 67) && (A != 83)) goto pos36700; + mem59 = X; + goto pos36791; + + // -------------- + +pos37004: + Code37055(mem59); + A = A & 32; + if(A == 0) goto pos36700; + +pos37014: + mem59 = X; + goto pos36791; + + // -------------- + +pos37019: + X = mem59; + X--; + A = inputtemp[X]; + if((A == 'E') || (A == 'I') || (A == 'Y')) goto pos37014; + goto pos36700; + // -------------- + +pos37040: + Code37055(mem59); + A = A & 32; + if(A == 0) goto pos36791; + mem59 = X; + goto pos37040; + + //--------------------------------------- + +pos37077: + X = mem58 + 1; + A = inputtemp[X]; + if(A != 'E') goto pos37157; + X++; + Y = inputtemp[X]; + X--; + A = tab36376[Y] & 128; + if(A == 0) goto pos37108; + X++; + A = inputtemp[X]; + if(A != 'R') goto pos37113; +pos37108: + mem58 = X; + goto pos37184; +pos37113: + if((A == 83) || (A == 68)) goto pos37108; // 'S' 'D' + if(A != 76) goto pos37135; // 'L' + X++; + A = inputtemp[X]; + if(A != 89) goto pos36700; + goto pos37108; + +pos37135: + if(A != 70) goto pos36700; + X++; + A = inputtemp[X]; + if(A != 85) goto pos36700; + X++; + A = inputtemp[X]; + if(A == 76) goto pos37108; + goto pos36700; + +pos37157: + if(A != 73) goto pos36700; + X++; + A = inputtemp[X]; + if(A != 78) goto pos36700; + X++; + A = inputtemp[X]; + if(A == 71) goto pos37108; + //pos37177: + goto pos36700; + + // ----------------------------------------- + +pos37180: + + A = mem60; + mem58 = A; + +pos37184: + Y = mem65 + 1; + + //37187: CPY 64 + // if(? != 0) goto pos37194; + if(Y == mem64) goto pos37455; + mem65 = Y; + //37196: LDA (62),y + A = GetRuleByte(mem62, Y); + mem57 = A; + X = A; + A = tab36376[X] & 128; + if(A == 0) goto pos37226; + X = mem58 + 1; + A = inputtemp[X]; + if(A != mem57) goto pos36700; + mem58 = X; + goto pos37184; +pos37226: + A = mem57; + if(A == 32) goto pos37295; // ' ' + if(A == 35) goto pos37310; // '#' + if(A == 46) goto pos37320; // '.' + if(A == 38) goto pos37335; // '&' + if(A == 64) goto pos37367; // '' + if(A == 94) goto pos37404; // '' + if(A == 43) goto pos37419; // '+' + if(A == 58) goto pos37440; // ':' + if(A == 37) goto pos37077; // '%' + //pos37291: + // Code42041(); //Error + //37294: BRK + return 0; + + // -------------- +pos37295: + Code37066(mem58); + A = A & 128; + if(A != 0) goto pos36700; +pos37305: + mem58 = X; + goto pos37184; + + // -------------- + +pos37310: + Code37066(mem58); + A = A & 64; + if(A != 0) goto pos37305; + goto pos36700; + + // -------------- + +pos37320: + Code37066(mem58); + A = A & 8; + if(A == 0) goto pos36700; + +pos37330: + mem58 = X; + goto pos37184; + + // -------------- + +pos37335: + Code37066(mem58); + A = A & 16; + if(A != 0) goto pos37330; + A = inputtemp[X]; + if(A != 72) goto pos36700; + X++; + A = inputtemp[X]; + if((A == 67) || (A == 83)) goto pos37330; + goto pos36700; + + // -------------- + +pos37367: + Code37066(mem58); + A = A & 4; + if(A != 0) goto pos37330; + A = inputtemp[X]; + if(A != 72) goto pos36700; + if((A != 84) && (A != 67) && (A != 83)) goto pos36700; + mem58 = X; + goto pos37184; + + // -------------- + +pos37404: + Code37066(mem58); + A = A & 32; + if(A == 0) goto pos36700; +pos37414: + mem58 = X; + goto pos37184; + + // -------------- + +pos37419: + X = mem58; + X++; + A = inputtemp[X]; + if((A == 69) || (A == 73) || (A == 89)) goto pos37414; + goto pos36700; + + // ---------------------- + +pos37440: + + Code37066(mem58); + A = A & 32; + if(A == 0) goto pos37184; + mem58 = X; + goto pos37440; +pos37455: + Y = mem64; + mem61 = mem60; + +pos37461: + //37461: LDA (62),y + A = GetRuleByte(mem62, Y); + mem57 = A; + A = A & 127; + if(A != '=') { + mem56++; + X = mem56; + input[X] = A; + } + + //37478: BIT 57 + //37480: BPL 37485 //not negative flag + if((mem57 & 128) == 0) goto pos37485; //??? + goto pos36554; +pos37485: + Y++; + goto pos37461; +} + +// Constructor + +STM32SAM::STM32SAM(uint32_t STM32SAM_SPEED /* = 5 */) { + STM32SAM_SPEED = STM32SAM_SPEED & 0x1f; // limit it from 0 to 31 + + _STM32SAM_SPEED = STM32SAM_SPEED; + + // set default voice + + speed = 72; + pitch = 64; + mouth = 128; + throat = 128; + + phonetic = 0; + singmode = 0; + + wait1 = 7; + wait2 = 6; + + mem59 = 0; + + oldtimetableindex = 0; +} + +STM32SAM::STM32SAM() { + _STM32SAM_SPEED = 7; + + // set default voice + + speed = 72; + pitch = 64; + mouth = 128; + throat = 128; + + phonetic = 0; + singmode = 0; + + wait1 = 7; + wait2 = 6; + + mem59 = 0; + + oldtimetableindex = 0; +} + +/* + STM32SAM::~STM32SAM() { + { + // TODO: end(); + } +*/ + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sam (variable string, phonetic, sing, pitch, speed, mouth, throat) +// STM32SAM say (sing off, phonetic off) (const string) +// STM32SAM say (sing off, phonetic off) (variable string) +// STM32SAM sing (sing on, phonetic off) (const string) +// STM32SAM sing (sing on, phonetic off) (variable string) +// STM32SAM sayPhonetic (sing off, phonetic on) (const string) +// STM32SAM sayPhonetic (sing off, phonetic on) (variable string) +// STM32SAM singPhonetic (sing on, phonetic on) (const string) +// STM32SAM singPhonetic (sing on, phonetic on) (variable string) +// STM32SAM voice (pitch, speed, mouth, throat) +// STM32SAM setPitch (pitch) +// STM32SAM setSpeed (speed) +// STM32SAM setMouth (mouth) +// STM32SAM setThroat (throat) +// +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sam (const string, phonetic, sing, pitch, speed, mouth, throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +char to_upper_case(char c) { + if(c >= 'a' && c <= 'z') { + return c - 'a' + 'A'; + } + return c; +} + +void STM32SAM::sam( + const char* argv, + unsigned char _phonetic, + unsigned char _singmode, + unsigned char _pitch, + unsigned char _speed, + unsigned char _mouth, + unsigned char _throat) { + phonetic = _phonetic; + singmode = _singmode; + pitch = _pitch; + speed = _speed; + mouth = _mouth; + throat = _throat; + + int i; + + for(i = 0; i < 256; i++) { + input[i] = argv[i]; + } + + for(i = 0; input[i] != 0; i++) { + if(i != 0) { + input[i] = to_upper_case((int)argv[i]); + } + } + + if(!phonetic) { + strncat(input, "[", 256); + if(!TextToPhonemes((unsigned char*)input)) { + // PrintUsage(); + return; + } + + } else { + strncat(input, "\x9b", 256); + } + + SetInput(input); + + if(!SAMMain()) { + return; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sam (variable string, phonetic, sing, pitch, speed, mouth, throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::sam( + char* argv, + unsigned char _phonetic, + unsigned char _singmode, + unsigned char _pitch, + unsigned char _speed, + unsigned char _mouth, + unsigned char _throat) { + phonetic = _phonetic; + singmode = _singmode; + pitch = _pitch; + speed = _speed; + mouth = _mouth; + throat = _throat; + + int i; + + for(i = 0; i < 256; i++) { + input[i] = argv[i]; + } + + for(i = 0; input[i] != 0; i++) { + if(i != 0) { + input[i] = to_upper_case((int)argv[i]); + } + } + + if(!phonetic) { + strncat(input, "[", 256); + if(!TextToPhonemes((unsigned char*)input)) { + // PrintUsage(); + return; + } + + } else { + strncat(input, "\x9b", 256); + } + + SetInput(input); + + if(!SAMMain()) { + return; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM say(sing off, phonetic off) (const string) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::say(const char* argv) { + int i; + + phonetic = 0; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::say(char* argv) { + int i; + + phonetic = 0; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sing (sing on, phonetic off) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::sing(const char* argv) { + int i; + + phonetic = 0; + singmode = 1; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::sing(char* argv) { + int i; + + phonetic = 0; + singmode = 1; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sayPhonetic (sing off, phonetic on) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::sayPhonetic(const char* argv) { + int i; + + phonetic = 1; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::sayPhonetic(char* argv) { + int i; + + phonetic = 1; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM singPhonetic (sing on, phonetic on) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::singPhonetic(const char* argv) { + int i; + + phonetic = 1; + singmode = 1; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::singPhonetic(char* argv) { + int i; + + phonetic = 1; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM voice (pitch, speed, mouth, throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setVoice( + unsigned char _pitch /* = 64 */, + unsigned char _speed /* = 72 */, + unsigned char _mouth /* = 128 */, + unsigned char _throat /* = 128 */) { + pitch = _pitch; + speed = _speed; + mouth = _mouth; + throat = _throat; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setPitch (pitch) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setPitch(unsigned char _pitch /* = 64 */) { + pitch = _pitch; +} +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setSpeed (speed) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setSpeed(unsigned char _speed /* = 72 */) { + speed = _speed; +} +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setMouth (mouth) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setMouth(unsigned char _mouth /* = 128 */) { + mouth = _mouth; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setThroat (throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setThroat(unsigned char _throat /* = 128 */) { + throat = _throat; +} +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Hardware +// +//////////////////////////////////////////////////////////////////////////////////////////// +// Hardware specifics, for easier porting to other microcontrollers + +// +// Set PA8 pin as PWM, at 256 timer ticks overflow (8bit resolution) + +#include +#include + +#define FURI_HAL_SPEAKER_TIMER TIM16 +#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 + +void STM32SAM::begin(void) { +#ifdef USE_ROGER_CORE + + pinMode(PA8, PWM); // audio output pin + + Timer1.setPeriod( + 4); // Can't set at 256 ticks, only in uS. First nearest uS is 4 (Roger core is only for bluepill, that means 72*4=288 ticks, or 128*4=512 ticks when overclocked. It's ok, just overall volume will be lower, because maximum volume will be 256/288 or 256/512) + +#endif + +#ifdef USE_STM32duino_CORE + pinMode(PA8, OUTPUT); + + PWM->pause(); + PWM->setMode(1, TIMER_OUTPUT_COMPARE_PWM1, PA8); // TIM1 CH1 (PA8) + PWM->setPrescaleFactor(1); + PWM->setOverflow(256, TICK_FORMAT); // 256 ticks overflow, no matter the CPU (timer) speed + PWM->resume(); + +#endif + + LL_TIM_InitTypeDef TIM_InitStruct; + memset(&TIM_InitStruct, 0, sizeof(LL_TIM_InitTypeDef)); + TIM_InitStruct.Prescaler = 4; + TIM_InitStruct.Autoreload = 255; + LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct); + + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct; + memset(&TIM_OC_InitStruct, 0, sizeof(LL_TIM_OC_InitTypeDef)); + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 127; + LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); + LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER); +} // begin + +inline void STM32SAM::SetAUDIO(unsigned char main_volume) { +#ifdef USE_ROGER_CORE + Timer1.setCompare(TIMER_CH1, main_volume); +#endif + +#ifdef USE_STM32duino_CORE + PWM->setCaptureCompare(1, main_volume, TICK_COMPARE_FORMAT); +#endif + + // if(main_volume > 64) { + // LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, 127); + // } else { + // LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, main_volume); + // } + + float data = main_volume; + data /= 255.0f; + data -= 0.5f; + data *= 4.0f; + data = tanhf(data); + + data += 0.5f; + data *= 255.0f; + + if(data < 0) { + data = 0; + } else if(data > 255) { + data = 255; + } + + LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, data); +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/sam/stm32_sam.h b/Applications/Official/DEV_FW/source/xMasterX/sam/stm32_sam.h new file mode 100644 index 000000000..910227ac3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/sam/stm32_sam.h @@ -0,0 +1,96 @@ +#include + +#ifndef __STM32SAM__ +#define __STM32SAM__ + +// SAM Text-To-Speech (TTS), ported from https://github.com/s-macke/SAM + +class STM32SAM { +public: + STM32SAM(uint32_t STM32SAM_SPEED); + STM32SAM(); + + void begin(void); + + void + sam(const char* argv, + unsigned char phonetic, + unsigned char singmode, + unsigned char pitch, + unsigned char speed, + unsigned char mouth, + unsigned char throat); + void + sam(char* argv, + unsigned char phonetic, + unsigned char singmode, + unsigned char pitch, + unsigned char speed, + unsigned char mouth, + unsigned char throat); + + void say(const char* argv); + void say(char* argv); + void sing(const char* argv); + void sing(char* argv); + void sayPhonetic(const char* argv); + void sayPhonetic(char* argv); + void singPhonetic(const char* argv); + void singPhonetic(char* argv); + void setVoice( + unsigned char _pitch = 64, + unsigned char _speed = 72, + unsigned char _mouth = 128, + unsigned char _throat = 128); + void setPitch(unsigned char _pitch = 64); + void setSpeed(unsigned char _speed = 72); + void setMouth(unsigned char _mouth = 128); + void setThroat(unsigned char _throat = 128); + +private: + void SetAUDIO(unsigned char main_volume); + + void Output8BitAry(int index, unsigned char ary[5]); + void Output8Bit(int index, unsigned char A); + unsigned char Read(unsigned char p, unsigned char Y); + void Write(unsigned char p, unsigned char Y, unsigned char value); + void RenderSample(unsigned char* mem66); + void Render(); + void AddInflection(unsigned char mem48, unsigned char phase1); + void SetMouthThroat(); + unsigned char trans(unsigned char mem39212, unsigned char mem39213); + void SetInput(char* _input); + void Init(); + int SAMMain(); + void PrepareOutput(); + void Insert( + unsigned char position /*var57*/, + unsigned char mem60, + unsigned char mem59, + unsigned char mem58); + void InsertBreath(); + void CopyStress(); + int Parser1(); + void SetPhonemeLength(); + void Code41240(); + void Parser2(); + void AdjustLengths(); + void Code47503(unsigned char mem52); + void Code37055(unsigned char mem59); + void Code37066(unsigned char mem58); + unsigned char GetRuleByte(unsigned short mem62, unsigned char Y); + int TextToPhonemes(unsigned char* input); // Code36484 + + uint32_t _STM32SAM_SPEED; + + unsigned char speed; + unsigned char pitch; + unsigned char mouth; + unsigned char throat; + + unsigned char phonetic; + unsigned char singmode; + +}; // STM32SAM class + +#endif \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/README.md b/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/README.md new file mode 100644 index 000000000..1c1970c19 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/README.md @@ -0,0 +1,35 @@ +# Scorched tanks - flipper zero game +A flipper zero game inspired by scorched earth. + +Current state is shown below: + +![input](scorched_tanks_v1.gif) + +## How to do: +Do not hesitate to create PRs. If you start working on sth, please start branch name with TODO id (e.g. `feature/2-change-tank-icon`) + +If you see an improvement, modify this readme and add suggestions via PR. Bugs can be reported via Issues + +## What to do next (it's not in the priority order): +5. flatten surface beneath tanks +7. explosion animation +9. sub-ghz multi-player +11. add other types of weapons +12. code AI +13. add terain destruction +14. add terain gravity (fall down after hitting the middle of the mountain) +18. Add menu with settings (vibartion on/off, difficulty) +20. add more ground modifiers (currently there is only one hill in the middle, maybe 2 hills, skew map, etc) + +## What have been done: +1. ~~remove movement~~ +2. ~~change tank icon~~ +3. ~~power as variable not constant~~ +4. ~~better map generation: high, low regions~~ (continuation in point 20.) +6. ~~collision with the enemy~~ +8. ~~local multi-player~~ +10. ~~improve projectile trace draw on angles > 80~~ +15. ~~FIX: firing stops when bullet fly above the screen~~ +16. ~~Slightly randomize player and enemy spawn locations~~ +17. ~~Shooting vibration~~ +19. ~~Add wind~~ diff --git a/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/application.fam b/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/application.fam new file mode 100644 index 000000000..58f8863d4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/application.fam @@ -0,0 +1,12 @@ +App( + appid="Scorched_Tanks", + name="Scorched Tanks", + apptype=FlipperAppType.EXTERNAL, + entry_point="scorched_tanks_game_app", + cdefines=["APP_SCORCHED_TANKS_GAME"], + requires=["gui"], + stack_size=1 * 1024, + order=100, + fap_icon="scorchedTanks_10px.png", + fap_category="Games_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/scorchedTanks_10px.png b/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/scorchedTanks_10px.png new file mode 100644 index 000000000..6e1ae4c04 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/scorchedTanks_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/scorched_tanks_game_app.c b/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/scorched_tanks_game_app.c new file mode 100644 index 000000000..fd4c73ee7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/scorched_tanks_game_app.c @@ -0,0 +1,540 @@ +#include +#include +#include +#include +#include +#include +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define PLAYER_INIT_LOCATION_X 20 +#define PLAYER_INIT_AIM 45 +#define PLAYER_INIT_POWER 50 +#define ENEMY_INIT_LOCATION_X 108 +#define TANK_BARREL_LENGTH 8 +#define GRAVITY_FORCE (double)0.5 +#define MIN_GROUND_HEIGHT 35 +#define MAX_GROUND_HEIGHT 55 +#define MAX_FIRE_POWER 100 +#define MIN_FIRE_POWER 0 +#define TANK_COLLIDER_SIZE 3 +#define MAX_WIND 10 +#define MAX_PLAYER_DIFF_X 20 +#define MAX_ENEMY_DIFF_X 20 + +// That's a filthy workaround but sin(player.aimAngle) breaks it all... If you're able to fix it, please do create a PR! +double scorched_tanks_sin[91] = { + 0.000, -0.017, -0.035, -0.052, -0.070, -0.087, -0.105, -0.122, -0.139, -0.156, -0.174, -0.191, + -0.208, -0.225, -0.242, -0.259, -0.276, -0.292, -0.309, -0.326, -0.342, -0.358, -0.375, -0.391, + -0.407, -0.423, -0.438, -0.454, -0.469, -0.485, -0.500, -0.515, -0.530, -0.545, -0.559, -0.574, + -0.588, -0.602, -0.616, -0.629, -0.643, -0.656, -0.669, -0.682, -0.695, -0.707, -0.719, -0.731, + -0.743, -0.755, -0.766, -0.777, -0.788, -0.799, -0.809, -0.819, -0.829, -0.839, -0.848, -0.857, + -0.866, -0.875, -0.883, -0.891, -0.899, -0.906, -0.914, -0.921, -0.927, -0.934, -0.940, -0.946, + -0.951, -0.956, -0.961, -0.966, -0.970, -0.974, -0.978, -0.982, -0.985, -0.988, -0.990, -0.993, + -0.995, -0.996, -0.998, -0.999, -0.999, -1.000, -1.000}; +double scorched_tanks_cos[91] = { + 1.000, 1.000, 0.999, 0.999, 0.998, 0.996, 0.995, 0.993, 0.990, 0.988, 0.985, 0.982, 0.978, + 0.974, 0.970, 0.966, 0.961, 0.956, 0.951, 0.946, 0.940, 0.934, 0.927, 0.921, 0.914, 0.906, + 0.899, 0.891, 0.883, 0.875, 0.866, 0.857, 0.848, 0.839, 0.829, 0.819, 0.809, 0.799, 0.788, + 0.777, 0.766, 0.755, 0.743, 0.731, 0.719, 0.707, 0.695, 0.682, 0.669, 0.656, 0.643, 0.629, + 0.616, 0.602, 0.588, 0.574, 0.559, 0.545, 0.530, 0.515, 0.500, 0.485, 0.469, 0.454, 0.438, + 0.423, 0.407, 0.391, 0.375, 0.358, 0.342, 0.326, 0.309, 0.292, 0.276, 0.259, 0.242, 0.225, + 0.208, 0.191, 0.174, 0.156, 0.139, 0.122, 0.105, 0.087, 0.070, 0.052, 0.035, 0.017, 0.000}; +double scorched_tanks_tan[91] = { + 0.000, -0.017, -0.035, -0.052, -0.070, -0.087, -0.105, -0.123, -0.141, -0.158, -0.176, + -0.194, -0.213, -0.231, -0.249, -0.268, -0.287, -0.306, -0.325, -0.344, -0.364, -0.384, + -0.404, -0.424, -0.445, -0.466, -0.488, -0.510, -0.532, -0.554, -0.577, -0.601, -0.625, + -0.649, -0.674, -0.700, -0.727, -0.754, -0.781, -0.810, -0.839, -0.869, -0.900, -0.932, + -0.966, -1.000, -1.036, -1.072, -1.111, -1.150, -1.192, -1.235, -1.280, -1.327, -1.376, + -1.428, -1.483, -1.540, -1.600, -1.664, -1.732, -1.804, -1.881, -1.963, -2.050, -2.144, + -2.246, -2.356, -2.475, -2.605, -2.747, -2.904, -3.078, -3.271, -3.487, -3.732, -4.011, + -4.331, -4.704, -5.144, -5.671, -6.313, -7.115, -8.144, -9.513, -11.429, -14.298, -19.077, + -28.627, -57.254, -90747.269}; +unsigned char scorched_tanks_ground_modifiers[SCREEN_WIDTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 28, 26, 24, 22, 20, + 18, 16, 14, 12, 10, 8, 6, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +typedef struct { + // +-----x + // | + // | + // y + uint8_t x; + uint8_t y; +} Point; + +typedef struct { + // +-----x + // | + // | + // y + double x; + double y; +} PointDetailed; + +typedef struct { + unsigned char locationX; + unsigned char hp; + int aimAngle; + unsigned char firePower; +} Tank; + +typedef struct { + Point ground[SCREEN_WIDTH]; + Tank player; + Tank enemy; + bool isPlayerTurn; + bool isShooting; + int windSpeed; + Point trajectory[SCREEN_WIDTH]; + unsigned char trajectoryAnimationStep; + PointDetailed bulletPosition; + PointDetailed bulletVector; +} Game; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} ScorchedTanksEvent; + +int scorched_tanks_random(int min, int max) { + return min + rand() % ((max + 1) - min); +} + +void scorched_tanks_generate_ground(Game* game_state) { + int lastHeight = 45; + + for(unsigned char a = 0; a < SCREEN_WIDTH; a++) { + int diffHeight = scorched_tanks_random(-2, 3); + int changeLength = scorched_tanks_random(1, 6); + + if(diffHeight == 0) { + changeLength = 1; + } + + for(int b = 0; b < changeLength; b++) { + if(a + b < SCREEN_WIDTH) { + int index = a + b; + int newPoint = lastHeight + diffHeight; + newPoint = newPoint < MIN_GROUND_HEIGHT ? MIN_GROUND_HEIGHT : newPoint; + newPoint = newPoint > MAX_GROUND_HEIGHT ? MAX_GROUND_HEIGHT : newPoint; + game_state->ground[index].x = index; + game_state->ground[index].y = newPoint - scorched_tanks_ground_modifiers[a]; + lastHeight = newPoint; + } else { + a += b; + break; + } + } + + a += changeLength - 1; + } +} + +void scorched_tanks_init_game(Game* game_state) { + game_state->player.locationX = PLAYER_INIT_LOCATION_X + + scorched_tanks_random(0, MAX_PLAYER_DIFF_X) - + MAX_PLAYER_DIFF_X / 2; + game_state->player.aimAngle = PLAYER_INIT_AIM; + game_state->player.firePower = PLAYER_INIT_POWER; + game_state->enemy.aimAngle = PLAYER_INIT_AIM; + game_state->enemy.firePower = PLAYER_INIT_POWER; + game_state->enemy.locationX = + ENEMY_INIT_LOCATION_X + scorched_tanks_random(0, MAX_ENEMY_DIFF_X) - MAX_ENEMY_DIFF_X / 2; + game_state->isPlayerTurn = true; + + game_state->windSpeed = scorched_tanks_random(0, MAX_WIND); + + for(int x = 0; x < SCREEN_WIDTH; x++) { + game_state->trajectory[x].x = 0; + game_state->trajectory[x].y = 0; + } + + scorched_tanks_generate_ground(game_state); +} + +void scorched_tanks_calculate_trajectory(Game* game_state) { + if(game_state->isShooting) { + game_state->bulletVector.x += ((double)game_state->windSpeed - MAX_WIND / 2) / 40; + game_state->bulletVector.y += GRAVITY_FORCE; + + game_state->bulletPosition.x += game_state->bulletVector.x; + game_state->bulletPosition.y += game_state->bulletVector.y; + + int totalDistanceToEnemy = 100; + + if(game_state->isPlayerTurn) { + double distanceToEnemyX = game_state->enemy.locationX - game_state->bulletPosition.x; + double distanceToEnemyY = game_state->ground[game_state->enemy.locationX].y - + TANK_COLLIDER_SIZE - game_state->bulletPosition.y; + totalDistanceToEnemy = + sqrt(distanceToEnemyX * distanceToEnemyX + distanceToEnemyY * distanceToEnemyY); + } else { + double distanceToEnemyX = game_state->player.locationX - game_state->bulletPosition.x; + double distanceToEnemyY = game_state->ground[game_state->player.locationX].y - + TANK_COLLIDER_SIZE - game_state->bulletPosition.y; + totalDistanceToEnemy = + sqrt(distanceToEnemyX * distanceToEnemyX + distanceToEnemyY * distanceToEnemyY); + } + + if(totalDistanceToEnemy <= TANK_COLLIDER_SIZE) { + game_state->isShooting = false; + scorched_tanks_init_game(game_state); + game_state->isPlayerTurn = !game_state->isPlayerTurn; + return; + } + + if(game_state->bulletPosition.x > SCREEN_WIDTH || + game_state->bulletPosition.y > + game_state->ground[(int)round(game_state->bulletPosition.x)].y) { + game_state->isShooting = false; + game_state->bulletPosition.x = 0; + game_state->bulletPosition.y = 0; + game_state->windSpeed = scorched_tanks_random(0, MAX_WIND); + game_state->isPlayerTurn = !game_state->isPlayerTurn; + return; + } + + if(game_state->bulletPosition.y > 0) { + game_state->trajectory[game_state->trajectoryAnimationStep].x = + round(game_state->bulletPosition.x); + game_state->trajectory[game_state->trajectoryAnimationStep].y = + round(game_state->bulletPosition.y); + game_state->trajectoryAnimationStep++; + } + } +} + +static void scorched_tanks_draw_tank( + Canvas* const canvas, + unsigned char x, + unsigned char y, + bool isPlayer) { + unsigned char lineIndex = 0; + + if(isPlayer) { + // Draw tank base + canvas_draw_line(canvas, x - 3, y - lineIndex, x + 3, y - lineIndex++); + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex++); + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex++); + + // draw turret + canvas_draw_line(canvas, x - 2, y - lineIndex, x + 1, y - lineIndex++); + canvas_draw_line(canvas, x - 2, y - lineIndex, x, y - lineIndex++); + } else { + // Draw tank base + canvas_draw_line(canvas, x - 3, y - lineIndex, x + 3, y - lineIndex++); + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex++); + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex++); + + // draw turret + canvas_draw_line(canvas, x - 1, y - lineIndex, x + 2, y - lineIndex++); + canvas_draw_line(canvas, x, y - lineIndex, x + 2, y - lineIndex++); + } +} + +static void scorched_tanks_render_callback(Canvas* const canvas, void* ctx) { + const Game* game_state = acquire_mutex((ValueMutex*)ctx, 25); + + if(game_state == NULL) { + return; + } + + canvas_draw_frame(canvas, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + canvas_set_color(canvas, ColorBlack); + + if(game_state->isShooting) { + canvas_draw_dot(canvas, game_state->bulletPosition.x, game_state->bulletPosition.y); + } + + for(int a = 1; a < SCREEN_WIDTH; a++) { + canvas_draw_line( + canvas, + game_state->ground[a - 1].x, + game_state->ground[a - 1].y, + game_state->ground[a].x, + game_state->ground[a].y); + + if(game_state->trajectory[a].y != 0) { + canvas_draw_dot(canvas, game_state->trajectory[a].x, game_state->trajectory[a].y); + } + } + + scorched_tanks_draw_tank( + canvas, + game_state->enemy.locationX, + game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE, + true); + + scorched_tanks_draw_tank( + canvas, + game_state->player.locationX, + game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE, + false); + + int aimX1 = 0; + int aimY1 = 0; + int aimX2 = 0; + int aimY2 = 0; + + if(game_state->isPlayerTurn) { + aimX1 = game_state->player.locationX; + aimY1 = game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE; + + double sinFromAngle = scorched_tanks_sin[game_state->player.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->player.aimAngle]; + aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + + aimX1 += 1; + aimX2 += 1; + } else { + aimX1 = game_state->enemy.locationX; + aimY1 = game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE; + + double sinFromAngle = scorched_tanks_sin[game_state->enemy.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->enemy.aimAngle]; + aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + + aimX2 = aimX1 - (aimX2 - aimX1); + + aimX1 -= 1; + aimX2 -= 1; + } + + canvas_draw_line(canvas, aimX1, aimY1 - 3, aimX2, aimY2 - 3); + + canvas_set_font(canvas, FontSecondary); + + char buffer2[12]; + snprintf(buffer2, sizeof(buffer2), "wind: %i", game_state->windSpeed - MAX_WIND / 2); + canvas_draw_str(canvas, 55, 10, buffer2); + + if(game_state->isPlayerTurn) { + canvas_draw_str(canvas, 93, 10, "player1"); + + char buffer[12]; + snprintf(buffer, sizeof(buffer), "a: %u", game_state->player.aimAngle); + canvas_draw_str(canvas, 2, 10, buffer); + + snprintf(buffer, sizeof(buffer), "p: %u", game_state->player.firePower); + canvas_draw_str(canvas, 27, 10, buffer); + } else { + canvas_draw_str(canvas, 93, 10, "player2"); + + char buffer[12]; + snprintf(buffer, sizeof(buffer), "a: %u", game_state->enemy.aimAngle); + canvas_draw_str(canvas, 2, 10, buffer); + + snprintf(buffer, sizeof(buffer), "p: %u", game_state->enemy.firePower); + canvas_draw_str(canvas, 27, 10, buffer); + } + + release_mutex((ValueMutex*)ctx, game_state); +} + +static void scorched_tanks_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + ScorchedTanksEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void scorched_tanks_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + ScorchedTanksEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void scorched_tanks_increase_power(Game* game_state) { + if(game_state->player.firePower < MAX_FIRE_POWER && !game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.firePower < MAX_FIRE_POWER) { + game_state->player.firePower++; + } + + if(!game_state->isPlayerTurn && game_state->enemy.firePower < MAX_FIRE_POWER) { + game_state->enemy.firePower++; + } + } +} + +static void scorched_tanks_decrease_power(Game* game_state) { + if(game_state->player.firePower > MIN_FIRE_POWER && !game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.firePower > MIN_FIRE_POWER) { + game_state->player.firePower--; + } + + if(!game_state->isPlayerTurn && game_state->enemy.firePower > MIN_FIRE_POWER) { + game_state->enemy.firePower--; + } + } +} + +static void scorched_tanks_aim_up(Game* game_state) { + if(!game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.aimAngle < 90) { + game_state->player.aimAngle++; + } + + if(!game_state->isPlayerTurn && game_state->enemy.aimAngle < 90) { + game_state->enemy.aimAngle++; + } + } +} + +static void scorched_tanks_aim_down(Game* game_state) { + if(game_state->player.aimAngle > 0 && !game_state->isShooting) { + if(game_state->isPlayerTurn) { + game_state->player.aimAngle--; + } else { + game_state->enemy.aimAngle--; + } + } +} + +const NotificationSequence sequence_long_vibro = { + &message_vibro_on, + &message_delay_500, + &message_vibro_off, + NULL, +}; + +static void scorched_tanks_fire(Game* game_state) { + if(!game_state->isShooting) { + if(game_state->isPlayerTurn) { + double sinFromAngle = scorched_tanks_sin[game_state->player.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->player.aimAngle]; + unsigned char aimX1 = game_state->player.locationX; + unsigned char aimY1 = + game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE; + int aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + int aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + game_state->bulletPosition.x = aimX2; + game_state->bulletPosition.y = aimY2; + game_state->bulletVector.x = scorched_tanks_cos[game_state->player.aimAngle] * + ((double)game_state->player.firePower / 10); + game_state->bulletVector.y = scorched_tanks_sin[game_state->player.aimAngle] * + ((double)game_state->player.firePower / 10); + } else { + double sinFromAngle = scorched_tanks_sin[game_state->enemy.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->enemy.aimAngle]; + unsigned char aimX1 = game_state->enemy.locationX; + unsigned char aimY1 = + game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE; + int aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + int aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + aimX2 = aimX1 - (aimX2 - aimX1); + + game_state->bulletPosition.x = aimX2; + game_state->bulletPosition.y = aimY2; + game_state->bulletVector.x = -scorched_tanks_cos[game_state->enemy.aimAngle] * + ((double)game_state->enemy.firePower / 10); + game_state->bulletVector.y = scorched_tanks_sin[game_state->enemy.aimAngle] * + ((double)game_state->enemy.firePower / 10); + } + + game_state->trajectoryAnimationStep = 0; + + for(int x = 0; x < SCREEN_WIDTH; x++) { + game_state->trajectory[x].x = 0; + game_state->trajectory[x].y = 0; + } + + game_state->isShooting = true; + + NotificationApp* notification = furi_record_open("notification"); + notification_message(notification, &sequence_long_vibro); + notification_message(notification, &sequence_blink_white_100); + furi_record_close("notification"); + } +} + +int32_t scorched_tanks_game_app(void* p) { + UNUSED(p); + srand(DWT->CYCCNT); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(ScorchedTanksEvent)); + + Game* game_state = malloc(sizeof(Game)); + scorched_tanks_init_game(game_state); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, game_state, sizeof(ScorchedTanksEvent))) { + FURI_LOG_E("ScorchedTanks", "cannot create mutex\r\n"); + free(game_state); + return 255; + } + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, scorched_tanks_render_callback, &state_mutex); + view_port_input_callback_set(view_port, scorched_tanks_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(scorched_tanks_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, 2000); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + ScorchedTanksEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 50); + + if(event.type == EventTypeKey) { // && game->isPlayerTurn + if(event.input.type == InputTypeRepeat || event.input.type == InputTypeShort) { + switch(event.input.key) { + case InputKeyUp: + scorched_tanks_aim_up(game_state); + break; + case InputKeyDown: + scorched_tanks_aim_down(game_state); + break; + case InputKeyRight: + scorched_tanks_increase_power(game_state); + break; + case InputKeyLeft: + scorched_tanks_decrease_power(game_state); + break; + case InputKeyOk: + scorched_tanks_fire(game_state); + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + scorched_tanks_calculate_trajectory(game_state); + } + + view_port_update(view_port); + release_mutex(&state_mutex, game_state); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + free(game_state); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/scorched_tanks_v1.gif b/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/scorched_tanks_v1.gif new file mode 100644 index 000000000..45b2ce117 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/scorched_tanks/scorched_tanks_v1.gif differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/README.md b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/README.md new file mode 100644 index 000000000..2e95d1e76 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/README.md @@ -0,0 +1,55 @@ +Tama P1 Emulator for Flipper Zero +======================================= + +This is a tama P1 Emulator app for Flipper Zero, based on [TamaLIB](https://github.com/jcrona/tamalib/). + +![Alt Text](tama.gif) + +How to play +----------- +Create a `tama_p1` folder in your microSD card, and put the ROM as `rom.bin`. +Use a search engine to find the Tamagotchi ROM. There is a file named `a`. +Rename this to `rom.bin`. + +Left button is A, OK is B, and right button is C. Hold the back button to exit. +There is currently no saving, so your progress will be reset when you exit the +app. + +Building +-------- +Move this folder into flippers applications/plugins/tama_p1. + + +Launching the app, directly from console to flipper: +`./fbt launch_app APPSRC=applications\plugins\tama_p1` + +Run the following to compile icons: +``` +scripts/assets.py icons applications/tama_p1/icons applications/tama_p1/compiled +``` + +Note: you may also need to add `-Wno-unused-parameter` to `CCFLAGS` in +`site_cons/cc.scons` to suppress unused parameter errors in TamaLIB. + +Debugging +--------- +Using the serial script from [FlipperScripts](https://github.com/DroomOne/FlipperScripts/blob/main/serial_logger.py) +it is easy to add direct logging after running the appliation: +`python .\serial_logger.py` + +`./fbt launch_app APPSRC=applications\plugins\tama_p1; python .\serial_logger.py` + + +Implemented +----------- +- Basic emulation +- Input +- Sound +- Saving/Loading emaulator state (stored in `/ext/tama_p1/save.bin`) + +To-do +----- +- Slots +- In-game reset +- Test mode? +- Volume adjustment diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/application.fam b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/application.fam new file mode 100644 index 000000000..4e3c2d9af --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/application.fam @@ -0,0 +1,12 @@ +App( + appid="TAMA_P1", + name="TAMA P1", + apptype=FlipperAppType.EXTERNAL, + entry_point="tama_p1_app", + cdefines=["APP_TAMA_P1"], + requires=["gui", "storage"], + stack_size=1 * 1024, + order=215, + fap_icon="tamaIcon.png", + fap_category="Games_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/compiled/assets_icons.h b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/compiled/assets_icons.h new file mode 100644 index 000000000..aa2735b72 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/compiled/assets_icons.h @@ -0,0 +1,66 @@ +#include + +const uint8_t _I_icon_0_0[] = { + 0x01, 0x00, 0x1a, 0x00, 0x00, 0x0d, 0xaa, 0x1d, 0x7e, 0x00, 0x9c, 0x3e, 0xf9, 0x0f, 0x9e, + 0x43, 0xe3, 0x00, 0x12, 0x9c, 0x43, 0xa7, 0x10, 0xc9, 0xe4, 0x30, 0x0a, 0x31, 0x08, 0x60, +}; +const uint8_t* const _I_icon_0[] = {_I_icon_0_0}; + +const uint8_t _I_icon_1_0[] = { + 0x00, 0x00, 0x00, 0x40, 0x04, 0x04, 0x04, 0xf0, 0x11, 0xf9, 0x1b, 0xf8, 0x07, 0x8c, 0x06, + 0xed, 0x36, 0xac, 0x26, 0xe8, 0x02, 0x52, 0x0b, 0x02, 0x18, 0xe0, 0x01, 0xe0, 0x01, +}; +const uint8_t* const _I_icon_1[] = {_I_icon_1_0}; + +const uint8_t _I_icon_2_0[] = { + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x13, 0x00, 0x21, 0x3c, 0x21, 0x3e, 0x23, 0x3f, 0x9f, 0x1f, + 0xc0, 0x0f, 0xe0, 0x07, 0xf0, 0x01, 0x7c, 0x00, 0x1f, 0x00, 0x06, 0x00, 0x06, 0x00, +}; +const uint8_t* const _I_icon_2[] = {_I_icon_2_0}; + +const uint8_t _I_icon_3_0[] = { + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x1c, 0x40, 0x3a, 0xc0, 0x36, 0xf0, 0x37, 0x18, 0x2d, + 0x0c, 0x2b, 0x0e, 0x02, 0x1f, 0x06, 0x3e, 0x07, 0xfe, 0x00, 0x7f, 0x00, 0x18, 0x00, +}; +const uint8_t* const _I_icon_3[] = {_I_icon_3_0}; + +const uint8_t _I_icon_4_0[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0xc7, 0x3c, 0x82, 0x2f, 0xf2, 0x26, 0xc7, 0x2c, + 0x69, 0x28, 0x2f, 0x2c, 0xe7, 0x27, 0x02, 0x20, 0x02, 0x30, 0x06, 0x1c, 0xfc, 0x0f, +}; +const uint8_t* const _I_icon_4[] = {_I_icon_4_0}; + +const uint8_t _I_icon_5_0[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0xfe, 0x0f, 0x03, 0x38, 0xc9, 0x22, 0x9a, 0x32, + 0xa2, 0x28, 0x24, 0x2c, 0x21, 0x20, 0x61, 0x30, 0x21, 0x10, 0xf3, 0x11, 0x1e, 0x0f, +}; +const uint8_t* const _I_icon_5[] = {_I_icon_5_0}; + +const uint8_t _I_icon_6_0[] = { + 0x01, 0x00, 0x17, 0x00, 0x00, 0x44, 0x62, 0xfd, 0x38, 0xbf, 0xcf, 0xb7, 0xf3, 0xf8, + 0xfc, 0x6e, 0x3f, 0x1a, 0xff, 0xc0, 0x3f, 0xf0, 0x1f, 0xf4, 0x02, 0x71, 0x00, +}; +const uint8_t* const _I_icon_6[] = {_I_icon_6_0}; + +const uint8_t _I_icon_7_0[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x41, 0x0e, 0xc4, 0x1f, 0x94, 0x20, + 0x00, 0x21, 0x22, 0x1f, 0x1d, 0x0a, 0x63, 0x20, 0xde, 0x20, 0x80, 0x1f, 0x00, 0x0e, +}; +const uint8_t* const _I_icon_7[] = {_I_icon_7_0}; + +const Icon I_icon_0 = + {.width = 14, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_icon_0}; +const Icon I_icon_1 = + {.width = 14, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_icon_1}; +const Icon I_icon_2 = + {.width = 14, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_icon_2}; +const Icon I_icon_3 = + {.width = 14, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_icon_3}; +const Icon I_icon_4 = + {.width = 14, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_icon_4}; +const Icon I_icon_5 = + {.width = 14, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_icon_5}; +const Icon I_icon_6 = + {.width = 14, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_icon_6}; +const Icon I_icon_7 = + {.width = 14, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_icon_7}; \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/hal.c b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/hal.c new file mode 100644 index 000000000..211457803 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/hal.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include "tama.h" + +#define TAG_HAL "TamaLIB" + +static void* tama_p1_hal_malloc(u32_t size) { + return malloc(size); +} + +static void tama_p1_hal_free(void* ptr) { + free(ptr); +} + +static void tama_p1_hal_halt(void) { + g_ctx->halted = true; +} + +static bool_t tama_p1_hal_is_log_enabled(log_level_t level) { + switch(level) { + case LOG_ERROR: + return true; + case LOG_INFO: + return true; + case LOG_MEMORY: + return false; + case LOG_CPU: + return false; + default: + return false; + } +} + +static void tama_p1_hal_log(log_level_t level, char* buff, ...) { + if(!tama_p1_hal_is_log_enabled(level)) return; + + FuriString* string = furi_string_alloc(); + va_list args; + va_start(args, buff); + furi_string_cat_vprintf(string, buff, args); + va_end(args); + + switch(level) { + case LOG_ERROR: + FURI_LOG_E(TAG_HAL, "%s", furi_string_get_cstr(string)); + break; + case LOG_INFO: + FURI_LOG_I(TAG_HAL, "%s", furi_string_get_cstr(string)); + break; + case LOG_MEMORY: + break; + case LOG_CPU: + FURI_LOG_D(TAG_HAL, "%s", furi_string_get_cstr(string)); + break; + default: + FURI_LOG_D(TAG_HAL, "%s", furi_string_get_cstr(string)); + break; + } + + furi_string_free(string); +} + +static void tama_p1_hal_sleep_until(timestamp_t ts) { + while(true) { + uint32_t count = LL_TIM_GetCounter(TIM2); + uint32_t delay = ts - count; + // FURI_LOG_D(TAG, "delay: %x", delay); + // Stolen from furi_delay_until_tick + if(delay != 0 && 0 == (delay >> (8 * sizeof(uint32_t) - 1))) { + // Not the best place to release mutex, but this is the only place we know whether + // we're ahead or behind, otherwise around the step call we'll always have to + // delay a tick and run more and more behind. + furi_mutex_release(g_state_mutex); + furi_delay_tick(1); + while(furi_mutex_acquire(g_state_mutex, FuriWaitForever) != FuriStatusOk) + furi_delay_tick(1); + } else { + break; + } + } +} + +static timestamp_t tama_p1_hal_get_timestamp(void) { + return LL_TIM_GetCounter(TIM2); +} + +static void tama_p1_hal_update_screen(void) { + // Do nothing, covered by main loop +} + +static void tama_p1_hal_set_lcd_matrix(u8_t x, u8_t y, bool_t val) { + if(val) + g_ctx->framebuffer[y] |= 1 << x; + else + g_ctx->framebuffer[y] &= ~(1 << x); +} + +static void tama_p1_hal_set_lcd_icon(u8_t icon, bool_t val) { + if(val) + g_ctx->icons |= 1 << icon; + else + g_ctx->icons &= ~(1 << icon); +} + +static void tama_p1_hal_play_frequency(bool_t en) { + if(en) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(g_ctx->frequency, 0.5f); + } + } else { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } + } + + g_ctx->buzzer_on = en; +} + +static void tama_p1_hal_set_frequency(u32_t freq) { + g_ctx->frequency = freq / 10.0F; + if(g_ctx->buzzer_on) tama_p1_hal_play_frequency(true); +} + +static int tama_p1_hal_handler(void) { + // Do nothing + return 0; +} + +void tama_p1_hal_init(hal_t* hal) { + hal->malloc = tama_p1_hal_malloc; + hal->free = tama_p1_hal_free; + hal->halt = tama_p1_hal_halt; + hal->is_log_enabled = tama_p1_hal_is_log_enabled; + hal->log = tama_p1_hal_log; + hal->sleep_until = tama_p1_hal_sleep_until; + hal->get_timestamp = tama_p1_hal_get_timestamp; + hal->update_screen = tama_p1_hal_update_screen; + hal->set_lcd_matrix = tama_p1_hal_set_lcd_matrix; + hal->set_lcd_icon = tama_p1_hal_set_lcd_icon; + hal->set_frequency = tama_p1_hal_set_frequency; + hal->play_frequency = tama_p1_hal_play_frequency; + hal->handler = tama_p1_hal_handler; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/hal_types.h b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/hal_types.h new file mode 100644 index 000000000..d27e6dfbe --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/hal_types.h @@ -0,0 +1,35 @@ +/* + * TamaLIB - A hardware agnostic tama P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _HAL_TYPES_H_ +#define _HAL_TYPES_H_ + +#include + +typedef bool bool_t; +typedef uint8_t u4_t; +typedef uint8_t u5_t; +typedef uint8_t u8_t; +typedef uint16_t u12_t; +typedef uint16_t u13_t; +typedef uint32_t u32_t; +typedef uint32_t + timestamp_t; // WARNING: Must be an unsigned type to properly handle wrapping (u32 wraps in around 1h11m when expressed in us) + +#endif /* _HAL_TYPES_H_ */ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_0.png b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_0.png new file mode 100644 index 000000000..7b94d3511 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_0.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_1.png b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_1.png new file mode 100644 index 000000000..934cf7187 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_1.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_2.png b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_2.png new file mode 100644 index 000000000..b83e449d9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_2.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_3.png b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_3.png new file mode 100644 index 000000000..34f4db24d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_3.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_4.png b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_4.png new file mode 100644 index 000000000..fe02b83af Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_5.png b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_5.png new file mode 100644 index 000000000..889acd50e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_5.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_6.png b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_6.png new file mode 100644 index 000000000..57d2f181b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_6.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_7.png b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_7.png new file mode 100644 index 000000000..4ff6e504a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/icons/icon_7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tama.h b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tama.h new file mode 100644 index 000000000..e2a267443 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tama.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include "tamalib/tamalib.h" + +#define TAG "TamaP1" +#define TAMA_ROM_PATH EXT_PATH("tama_p1/rom.bin") +#define TAMA_SCREEN_SCALE_FACTOR 2 +#define TAMA_LCD_ICON_SIZE 14 +#define TAMA_LCD_ICON_MARGIN 1 + +#define STATE_FILE_MAGIC "TLST" +#define STATE_FILE_VERSION 2 +#define TAMA_SAVE_PATH EXT_PATH("tama_p1/save.bin") + + +typedef struct { + FuriThread* thread; + hal_t hal; + uint8_t* rom; + // 32x16 screen, perfectly represented through uint32_t + uint32_t framebuffer[16]; + uint8_t icons; + bool halted; + bool fast_forward_done; + bool buzzer_on; + float frequency; +} TamaApp; + +typedef enum { + EventTypeInput, + EventTypeTick, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} TamaEvent; + +extern TamaApp* g_ctx; +extern FuriMutex* g_state_mutex; + +void tama_p1_hal_init(hal_t* hal); diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamaIcon.png b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamaIcon.png new file mode 100644 index 000000000..1962b68ae Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamaIcon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tama_p1.c b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tama_p1.c new file mode 100644 index 000000000..7184638d7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tama_p1.c @@ -0,0 +1,530 @@ +#include +#include +#include +#include +#include +#include +#include "tamalib/tamalib.h" +#include "tama.h" +#include "compiled/assets_icons.h" + +TamaApp* g_ctx; +FuriMutex* g_state_mutex; + +static const Icon* icons_list[] = { + &I_icon_0, + &I_icon_1, + &I_icon_2, + &I_icon_3, + &I_icon_4, + &I_icon_5, + &I_icon_6, + &I_icon_7, +}; + +static void tama_p1_draw_callback(Canvas* const canvas, void* cb_ctx) { + furi_assert(cb_ctx); + + FuriMutex* const mutex = cb_ctx; + if(furi_mutex_acquire(mutex, 25) != FuriStatusOk) return; + + if(g_ctx->rom == NULL) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 30, 30, "No ROM"); + } else if(g_ctx->halted) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 30, 30, "Halted"); + } else { + // FURI_LOG_D(TAG, "Drawing frame"); + // Calculate positioning + uint16_t canv_width = canvas_width(canvas); + uint16_t canv_height = canvas_height(canvas); + uint16_t lcd_matrix_scaled_width = 32 * TAMA_SCREEN_SCALE_FACTOR; + uint16_t lcd_matrix_scaled_height = 16 * TAMA_SCREEN_SCALE_FACTOR; + // uint16_t lcd_matrix_top = 0; + uint16_t lcd_matrix_top = (canv_height - lcd_matrix_scaled_height) / 2; + uint16_t lcd_matrix_left = (canv_width - lcd_matrix_scaled_width) / 2; + + uint16_t lcd_icon_upper_top = lcd_matrix_top - TAMA_LCD_ICON_SIZE - TAMA_LCD_ICON_MARGIN; + uint16_t lcd_icon_upper_left = lcd_matrix_left; + uint16_t lcd_icon_lower_top = + lcd_matrix_top + lcd_matrix_scaled_height + TAMA_LCD_ICON_MARGIN; + uint16_t lcd_icon_lower_left = lcd_matrix_left; + uint16_t lcd_icon_spacing_horiz = + (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE; + + + uint16_t y = lcd_matrix_top; + for(uint8_t row = 0; row < 16; ++row) { + uint16_t x = lcd_matrix_left; + uint32_t row_pixels = g_ctx->framebuffer[row]; + for(uint8_t col = 0; col < 32; ++col) { + if(row_pixels & 1) { + canvas_draw_box( + canvas, x, y, TAMA_SCREEN_SCALE_FACTOR, TAMA_SCREEN_SCALE_FACTOR); + } + x += TAMA_SCREEN_SCALE_FACTOR; + row_pixels >>= 1; + } + y += TAMA_SCREEN_SCALE_FACTOR; + } + + // Start drawing icons + uint8_t lcd_icons = g_ctx->icons; + + // Draw top icons + y = lcd_icon_upper_top; + // y = 64 - TAMA_LCD_ICON_SIZE; + uint16_t x_ic = lcd_icon_upper_left; + for(uint8_t i = 0; i < 4; ++i) { + if(lcd_icons & 1) { + canvas_draw_icon(canvas, x_ic, y, icons_list[i]); + } + // x_ic += TAMA_LCD_ICON_SIZE + 4; + x_ic += lcd_icon_spacing_horiz; + lcd_icons >>= 1; + } + + // Draw bottom icons + y = lcd_icon_lower_top; + x_ic = lcd_icon_lower_left; + for(uint8_t i = 4; i < 8; ++i) { + // canvas_draw_frame(canvas, x_ic, y, TAMA_LCD_ICON_SIZE, TAMA_LCD_ICON_SIZE); + if(lcd_icons & 1) { + canvas_draw_icon(canvas, x_ic, y, icons_list[i]); + } + x_ic += lcd_icon_spacing_horiz; + lcd_icons >>= 1; + } + } + + furi_mutex_release(mutex); +} + +static void tama_p1_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + TamaEvent event = {.type = EventTypeInput, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void tama_p1_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + TamaEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void tama_p1_load_state() { + state_t *state; + uint8_t buf[4]; + bool error = false; + state = tamalib_get_state(); + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + + storage_file_read(file, &buf, 4); + if (buf[0] != (uint8_t) STATE_FILE_MAGIC[0] || buf[1] != (uint8_t) STATE_FILE_MAGIC[1] || + buf[2] != (uint8_t) STATE_FILE_MAGIC[2] || buf[3] != (uint8_t) STATE_FILE_MAGIC[3]) { + FURI_LOG_E(TAG, "FATAL: Wrong state file magic in \"%s\" !\n", TAMA_SAVE_PATH); + error = true; + } + + storage_file_read(file, &buf, 1); + if (buf[0] != STATE_FILE_VERSION) { + FURI_LOG_E(TAG, "FATAL: Unsupported version"); + error = true; + } + if (!error) { + FURI_LOG_D(TAG, "Reading save.bin"); + + storage_file_read(file, &buf, 2); + *(state->pc) = buf[0] | ((buf[1] & 0x1F) << 8); + + storage_file_read(file, &buf, 2); + *(state->x) = buf[0] | ((buf[1] & 0xF) << 8); + + storage_file_read(file, &buf, 2); + *(state->y) = buf[0] | ((buf[1] & 0xF) << 8); + + storage_file_read(file, &buf, 1); + *(state->a) = buf[0] & 0xF; + + storage_file_read(file, &buf, 1); + *(state->b) = buf[0] & 0xF; + + storage_file_read(file, &buf, 1); + *(state->np) = buf[0] & 0x1F; + + storage_file_read(file, &buf, 1); + *(state->sp) = buf[0]; + + storage_file_read(file, &buf, 1); + *(state->flags) = buf[0] & 0xF; + + storage_file_read(file, &buf, 4); + *(state->tick_counter) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + + storage_file_read(file, &buf, 4); + *(state->clk_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + + storage_file_read(file, &buf, 4); + *(state->prog_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + + storage_file_read(file, &buf, 1); + *(state->prog_timer_enabled) = buf[0] & 0x1; + + storage_file_read(file, &buf, 1); + *(state->prog_timer_data) = buf[0]; + + storage_file_read(file, &buf, 1); + *(state->prog_timer_rld) = buf[0]; + + storage_file_read(file, &buf, 4); + *(state->call_depth) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + + FURI_LOG_D(TAG, "Restoring Interupts"); + for (uint32_t i = 0; i < INT_SLOT_NUM; i++) { + storage_file_read(file, &buf, 1); + state->interrupts[i].factor_flag_reg = buf[0] & 0xF; + + storage_file_read(file, &buf, 1); + state->interrupts[i].mask_reg = buf[0] & 0xF; + + storage_file_read(file, &buf, 1); + state->interrupts[i].triggered = buf[0] & 0x1; + } + + /* First 640 half bytes correspond to the RAM */ + FURI_LOG_D(TAG, "Restoring RAM"); + for (uint32_t i = 0; i < MEM_RAM_SIZE; i++) { + storage_file_read(file, &buf, 1); + SET_RAM_MEMORY(state->memory, i + MEM_RAM_ADDR, buf[0] & 0xF); + } + + /* I/Os are from 0xF00 to 0xF7F */ + FURI_LOG_D(TAG, "Restoring I/O"); + for (uint32_t i = 0; i < MEM_IO_SIZE; i++) { + storage_file_read(file, &buf, 1); + SET_IO_MEMORY(state->memory, i + MEM_IO_ADDR, buf[0] & 0xF); + } + FURI_LOG_D(TAG, "Refreshing Hardware"); + tamalib_refresh_hw(); + } + } + + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + + +static void tama_p1_save_state() { + + // Saving state + FURI_LOG_D(TAG, "Saving Gamestate"); + uint8_t buf[4]; + state_t *state; + uint32_t offset = 0; + state = tamalib_get_state(); + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + buf[0] = (uint8_t) STATE_FILE_MAGIC[0]; + buf[1] = (uint8_t) STATE_FILE_MAGIC[1]; + buf[2] = (uint8_t) STATE_FILE_MAGIC[2]; + buf[3] = (uint8_t) STATE_FILE_MAGIC[3]; + offset += storage_file_write(file, &buf, sizeof(buf)); + + buf[0] = STATE_FILE_VERSION & 0xFF; + offset += storage_file_write(file, &buf, 1); + + buf[0] = *(state->pc) & 0xFF; + buf[1] = (*(state->pc) >> 8) & 0x1F; + offset += storage_file_write(file, &buf, 2); + + buf[0] = *(state->x) & 0xFF; + buf[1] = (*(state->x) >> 8) & 0xF; + offset += storage_file_write(file, &buf, 2); + + buf[0] = *(state->y) & 0xFF; + buf[1] = (*(state->y) >> 8) & 0xF; + offset += storage_file_write(file, &buf, 2); + + buf[0] = *(state->a) & 0xF; + offset += storage_file_write(file, &buf, 1); + + buf[0] = *(state->b) & 0xF; + offset += storage_file_write(file, &buf, 1); + + buf[0] = *(state->np) & 0x1F; + offset += storage_file_write(file, &buf, 1); + + buf[0] = *(state->sp) & 0xFF; + offset += storage_file_write(file, &buf, 1); + + buf[0] = *(state->flags) & 0xF; + offset += storage_file_write(file, &buf, 1); + + buf[0] = *(state->tick_counter) & 0xFF; + buf[1] = (*(state->tick_counter) >> 8) & 0xFF; + buf[2] = (*(state->tick_counter) >> 16) & 0xFF; + buf[3] = (*(state->tick_counter) >> 24) & 0xFF; + offset += storage_file_write(file, &buf, sizeof(buf)); + + buf[0] = *(state->clk_timer_timestamp) & 0xFF; + buf[1] = (*(state->clk_timer_timestamp) >> 8) & 0xFF; + buf[2] = (*(state->clk_timer_timestamp) >> 16) & 0xFF; + buf[3] = (*(state->clk_timer_timestamp) >> 24) & 0xFF; + offset += storage_file_write(file, &buf, sizeof(buf)); + + buf[0] = *(state->prog_timer_timestamp) & 0xFF; + buf[1] = (*(state->prog_timer_timestamp) >> 8) & 0xFF; + buf[2] = (*(state->prog_timer_timestamp) >> 16) & 0xFF; + buf[3] = (*(state->prog_timer_timestamp) >> 24) & 0xFF; + offset += storage_file_write(file, &buf, sizeof(buf)); + + buf[0] = *(state->prog_timer_enabled) & 0x1; + offset += storage_file_write(file, &buf, 1); + + buf[0] = *(state->prog_timer_data) & 0xFF; + offset += storage_file_write(file, &buf, 1); + + buf[0] = *(state->prog_timer_rld) & 0xFF; + offset += storage_file_write(file, &buf, 1); + + buf[0] = *(state->call_depth) & 0xFF; + buf[1] = (*(state->call_depth) >> 8) & 0xFF; + buf[2] = (*(state->call_depth) >> 16) & 0xFF; + buf[3] = (*(state->call_depth) >> 24) & 0xFF; + offset += storage_file_write(file, &buf, sizeof(buf)); + + for (uint32_t i = 0; i < INT_SLOT_NUM; i++) { + buf[0] = state->interrupts[i].factor_flag_reg & 0xF; + offset += storage_file_write(file, &buf, 1); + + buf[0] = state->interrupts[i].mask_reg & 0xF; + offset += storage_file_write(file, &buf, 1); + + buf[0] = state->interrupts[i].triggered & 0x1; + offset += storage_file_write(file, &buf, 1); + } + + /* First 640 half bytes correspond to the RAM */ + for (uint32_t i = 0; i < MEM_RAM_SIZE; i++) { + buf[0] = GET_RAM_MEMORY(state->memory, i + MEM_RAM_ADDR) & 0xF; + offset += storage_file_write(file, &buf, 1); + } + + /* I/Os are from 0xF00 to 0xF7F */ + for (uint32_t i = 0; i < MEM_IO_SIZE; i++) { + buf[0] = GET_IO_MEMORY(state->memory, i + MEM_IO_ADDR) & 0xF; + offset += storage_file_write(file, &buf, 1); + } + } + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + FURI_LOG_D(TAG, "Finished Writing %lu", offset); +} + + +static int32_t tama_p1_worker(void* context) { + bool running = true; + FuriMutex* mutex = context; + while(furi_mutex_acquire(mutex, FuriWaitForever) != FuriStatusOk) furi_delay_tick(1); + + cpu_sync_ref_timestamp(); + LL_TIM_EnableCounter(TIM2); + + tama_p1_load_state(); + + while(running) { + if(furi_thread_flags_get()) { + running = false; + } else { + // FURI_LOG_D(TAG, "Stepping"); + // for (int i = 0; i < 100; ++i) + tamalib_step(); + } + } + LL_TIM_DisableCounter(TIM2); + furi_mutex_release(mutex); + return 0; +} + + + +static void tama_p1_init(TamaApp* const ctx) { + g_ctx = ctx; + memset(ctx, 0, sizeof(TamaApp)); + tama_p1_hal_init(&ctx->hal); + + // Load ROM + Storage* storage = furi_record_open(RECORD_STORAGE); + FileInfo fi; + if(storage_common_stat(storage, TAMA_ROM_PATH, &fi) == FSE_OK) { + File* rom_file = storage_file_alloc(storage); + if(storage_file_open(rom_file, TAMA_ROM_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + ctx->rom = malloc((size_t)fi.size); + uint8_t* buf_ptr = ctx->rom; + size_t read = 0; + while(read < fi.size) { + size_t to_read = fi.size - read; + if(to_read > UINT16_MAX) to_read = UINT16_MAX; + uint16_t now_read = storage_file_read(rom_file, buf_ptr, (uint16_t)to_read); + read += now_read; + buf_ptr += now_read; + } + + // Reorder endianess of ROM + for(size_t i = 0; i < fi.size; i += 2) { + uint8_t b = ctx->rom[i]; + ctx->rom[i] = ctx->rom[i + 1]; + ctx->rom[i + 1] = b & 0xF; + } + } + + storage_file_close(rom_file); + storage_file_free(rom_file); + } + furi_record_close(RECORD_STORAGE); + + if(ctx->rom != NULL) { + // Init TIM2 + // 64KHz + LL_TIM_InitTypeDef tim_init = { + .Prescaler = 999, + .CounterMode = LL_TIM_COUNTERMODE_UP, + .Autoreload = 0xFFFFFFFF, + }; + LL_TIM_Init(TIM2, &tim_init); + LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableCounter(TIM2); + LL_TIM_SetCounter(TIM2, 0); + + // Init TamaLIB + tamalib_register_hal(&ctx->hal); + tamalib_init((u12_t*)ctx->rom, NULL, 64000); + tamalib_set_speed(1); + + // TODO: implement fast forwarding + ctx->fast_forward_done = true; + + // Start stepping thread + ctx->thread = furi_thread_alloc(); + furi_thread_set_name(ctx->thread, "TamaLIB"); + furi_thread_set_stack_size(ctx->thread, 1024); + furi_thread_set_callback(ctx->thread, tama_p1_worker); + furi_thread_set_context(ctx->thread, g_state_mutex); + furi_thread_start(ctx->thread); + } +} + +static void tama_p1_deinit(TamaApp* const ctx) { + if(ctx->rom != NULL) { + tamalib_release(); + furi_thread_free(ctx->thread); + free(ctx->rom); + } +} + +int32_t tama_p1_app(void* p) { + UNUSED(p); + + TamaApp* ctx = malloc(sizeof(TamaApp)); + g_state_mutex = furi_mutex_alloc(FuriMutexTypeRecursive); + tama_p1_init(ctx); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(TamaEvent)); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, tama_p1_draw_callback, g_state_mutex); + view_port_input_callback_set(view_port, tama_p1_input_callback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + FuriTimer* timer = + furi_timer_alloc(tama_p1_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 30); + + for(bool running = true; running;) { + TamaEvent event; + FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); + if(event_status == FuriStatusOk) { + // Local override with acquired context + if(furi_mutex_acquire(g_state_mutex, FuriWaitForever) != FuriStatusOk) continue; + + if(event.type == EventTypeTick) { + // FURI_LOG_D(TAG, "EventTypeTick"); + view_port_update(view_port); + } else if(event.type == EventTypeInput) { + FURI_LOG_D( + TAG, + "EventTypeInput: %ld %d %d", + event.input.sequence, + event.input.key, + event.input.type); + InputType input_type = event.input.type; + if(input_type == InputTypePress || input_type == InputTypeRelease) { + btn_state_t tama_btn_state = 0; + if(input_type == InputTypePress) + tama_btn_state = BTN_STATE_PRESSED; + else if(input_type == InputTypeRelease) + tama_btn_state = BTN_STATE_RELEASED; + + if(event.input.key == InputKeyLeft) { + tamalib_set_button(BTN_LEFT, tama_btn_state); + } else if(event.input.key == InputKeyOk) { + tamalib_set_button(BTN_MIDDLE, tama_btn_state); + } else if(event.input.key == InputKeyRight) { + tamalib_set_button(BTN_RIGHT, tama_btn_state); + } else if(event.input.key == InputKeyDown && event.input.type == InputTypeShort) { + // TODO: pause or fast-forward tamagotchi + tama_p1_save_state(); + } else if(event.input.key == InputKeyUp) { // mute tamagotchi + tamalib_set_button(BTN_LEFT, tama_btn_state); + tamalib_set_button(BTN_RIGHT, tama_btn_state); + } else if(event.input.key == InputKeyBack && event.input.type == InputTypeShort) { + tama_p1_save_state(); + } + } + + if(event.input.key == InputKeyBack && event.input.type == InputTypeLong) { + furi_timer_stop(timer); + running = false; + + tama_p1_save_state(); + } + } + + furi_mutex_release(g_state_mutex); + } else { + // Timeout + // FURI_LOG_D(TAG, "Timed out"); + } + } + + if(ctx->rom != NULL) { + furi_thread_flags_set(furi_thread_get_id(ctx->thread), 1); + furi_thread_join(ctx->thread); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_mutex_free(g_state_mutex); + tama_p1_deinit(ctx); + free(ctx); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/LICENSE new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/README.md b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/README.md new file mode 100644 index 000000000..add42da1a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/README.md @@ -0,0 +1,64 @@ +# TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + + +## Synopsis + +TamaLib is a hardware agnostic Tamagotchi P1 emulation library built from scratch. It is self-contained and aims at running on any platform powerful enough, from microcontrollers (MCUs) to desktop computers, thus spreading virtual life across the digital world. + +So far, it has been successfully implemented on different platforms: +- Desktop computers (check out [TamaTool](https://github.com/jcrona/tamatool/) for more information) +- STM32F072 MCU based board (check out [MCUGotchi](https://github.com/jcrona/mcugotchi/) for more information). +- OpenTama which is an STM32L072 MCU based board (check out [OpenTama](https://github.com/Sparkr-tech/opentama) and [MCUGotchi](https://github.com/jcrona/mcugotchi/) for more information). +- Arduino UNO (check out [ArduinoGotchi](https://github.com/GaryZ88/ArduinoGotchi/) for more information). + +## Importing TamaLIB + +TamaLIB cannot be used as is. In order to create life on a specific target, you need to import all TamaLIB related __.c__ and __.h__ files in your project (for instance in a __lib__ subfolder), to create a __hal_types.h__ file using the template provided and to implement the __hal_t__ structure, that will act as an abstraction layer between TamaLIB and your OS or SDK (detailed information can be found in __hal.h__). This abstraction layer basically connects TamaLIB to your target's buttons, clock, audio and screen, while also defining the C types that TamaLIB should use to represent 4-bit, 5-bit, 8-bit, 12-bit, 13-bit and 32-bit variables. Once done, you will be able to call the TamaLIB API from your project. + + +## Using the TamaLIB API + +Basically: +``` +/* ... */ + +/* Register the HAL */ +tamalib_register_hal(&my_hal); + +/* ... */ + +/* Initialize TamaLIB */ +tamalib_init(my_program, my_breakpoints, 1000000); // my_breakpoints can be NULL, 1000000 means that timestamps will be expressed in us + +/* ... */ + +/* Enter TamaLIB's loop */ +tamalib_mainloop(); + +/* ... */ + +/* Release TamaLIB */ +tamalib_release(); + +/* ... */ +``` +Your main project should then forward any button input to TamaLIB using the `tamalib_set_button()` function. + +As an alternative to `tamalib_mainloop()`, you can call `tamalib_step()` directly if your execution flow requires something more complex than a simple mainloop. In that case, TamaLIB will neither call the HAL `handler()` function, nor the HAL `update_screen()` function by itslef. + + +## License + +TamaLIB is distributed under the GPLv2 license. See the LICENSE file for more information. + + +## Hardware information + +The Tamagotchi P1 is based on an +[E0C6S46 Epson MCU](https://download.epson-europe.com/pub/electronics-de/asmic/4bit/62family/technicalmanual/tm_6s46.pdf), +and runs at 32,768 kHz. Its LCD is 32x16 B/W pixels, with 8 icons. +To my knowledge, the ROM available online has been extracted from a high-res picture of a die. The ROM mask was clear enough to be optically read. The pictures can be seen [there](https://siliconpr0n.org/map/bandai/tamagotchi-v1/) (thx asterick for the link !). +I would love to see the same work done on a P2 and add support for it in TamaLIB/TamaTool ! + +__ +Copyright (C) 2021 Jean-Christophe Rona diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/cpu.c b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/cpu.c new file mode 100644 index 000000000..3f422c2d6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/cpu.c @@ -0,0 +1,2033 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "cpu.h" +#include "hw.h" +#include "hal.h" + +#define TICK_FREQUENCY 32768 // Hz + +#define TIMER_1HZ_PERIOD 32768 // in ticks +#define TIMER_256HZ_PERIOD 128 // in ticks + +#define MASK_4B 0xF00 +#define MASK_6B 0xFC0 +#define MASK_7B 0xFE0 +#define MASK_8B 0xFF0 +#define MASK_10B 0xFFC +#define MASK_12B 0xFFF + +#define PCS (pc & 0xFF) +#define PCSL (pc & 0xF) +#define PCSH ((pc >> 4) & 0xF) +#define PCP ((pc >> 8) & 0xF) +#define PCB ((pc >> 12) & 0x1) +#define TO_PC(bank, page, step) ((step & 0xFF) | ((page & 0xF) << 8) | (bank & 0x1) << 12) +#define NBP ((np >> 4) & 0x1) +#define NPP (np & 0xF) +#define TO_NP(bank, page) ((page & 0xF) | (bank & 0x1) << 4) +#define XHL (x & 0xFF) +#define XL (x & 0xF) +#define XH ((x >> 4) & 0xF) +#define XP ((x >> 8) & 0xF) +#define YHL (y & 0xFF) +#define YL (y & 0xF) +#define YH ((y >> 4) & 0xF) +#define YP ((y >> 8) & 0xF) +#define M(n) get_memory(n) +#define SET_M(n, v) set_memory(n, v) +#define RQ(i) get_rq(i) +#define SET_RQ(i, v) set_rq(i, v) +#define SPL (sp & 0xF) +#define SPH ((sp >> 4) & 0xF) + +#define FLAG_C (0x1 << 0) +#define FLAG_Z (0x1 << 1) +#define FLAG_D (0x1 << 2) +#define FLAG_I (0x1 << 3) + +#define C !!(flags & FLAG_C) +#define Z !!(flags & FLAG_Z) +#define D !!(flags & FLAG_D) +#define I !!(flags & FLAG_I) + +#define SET_C() \ + { flags |= FLAG_C; } +#define CLEAR_C() \ + { flags &= ~FLAG_C; } +#define SET_Z() \ + { flags |= FLAG_Z; } +#define CLEAR_Z() \ + { flags &= ~FLAG_Z; } +#define SET_D() \ + { flags |= FLAG_D; } +#define CLEAR_D() \ + { flags &= ~FLAG_D; } +#define SET_I() \ + { flags |= FLAG_I; } +#define CLEAR_I() \ + { flags &= ~FLAG_I; } + +#define REG_CLK_INT_FACTOR_FLAGS 0xF00 +#define REG_SW_INT_FACTOR_FLAGS 0xF01 +#define REG_PROG_INT_FACTOR_FLAGS 0xF02 +#define REG_SERIAL_INT_FACTOR_FLAGS 0xF03 +#define REG_K00_K03_INT_FACTOR_FLAGS 0xF04 +#define REG_K10_K13_INT_FACTOR_FLAGS 0xF05 +#define REG_CLOCK_INT_MASKS 0xF10 +#define REG_SW_INT_MASKS 0xF11 +#define REG_PROG_INT_MASKS 0xF12 +#define REG_SERIAL_INT_MASKS 0xF13 +#define REG_K00_K03_INT_MASKS 0xF14 +#define REG_K10_K13_INT_MASKS 0xF15 +#define REG_PROG_TIMER_DATA_L 0xF24 +#define REG_PROG_TIMER_DATA_H 0xF25 +#define REG_PROG_TIMER_RELOAD_DATA_L 0xF26 +#define REG_PROG_TIMER_RELOAD_DATA_H 0xF27 +#define REG_K00_K03_INPUT_PORT 0xF40 +#define REG_K10_K13_INPUT_PORT 0xF42 +#define REG_K40_K43_BZ_OUTPUT_PORT 0xF54 +#define REG_CPU_OSC3_CTRL 0xF70 +#define REG_LCD_CTRL 0xF71 +#define REG_LCD_CONTRAST 0xF72 +#define REG_SVD_CTRL 0xF73 +#define REG_BUZZER_CTRL1 0xF74 +#define REG_BUZZER_CTRL2 0xF75 +#define REG_CLK_WD_TIMER_CTRL 0xF76 +#define REG_SW_TIMER_CTRL 0xF77 +#define REG_PROG_TIMER_CTRL 0xF78 +#define REG_PROG_TIMER_CLK_SEL 0xF79 + +#define INPUT_PORT_NUM 2 + +typedef struct { + char* log; + u12_t code; + u12_t mask; + u12_t shift_arg0; + u12_t mask_arg0; // != 0 only if there are two arguments + u8_t cycles; + void (*cb)(u8_t arg0, u8_t arg1); +} op_t; + +typedef struct { + u4_t states; +} input_port_t; + +/* Registers */ +static u13_t pc, next_pc; +static u12_t x, y; +static u4_t a, b; +static u5_t np; +static u8_t sp; + +/* Flags */ +static u4_t flags; + +static const u12_t* g_program = NULL; +static MEM_BUFFER_TYPE memory[MEM_BUFFER_SIZE]; + +static input_port_t inputs[INPUT_PORT_NUM] = {{0}}; + +/* Interrupts (in priority order) */ +static interrupt_t interrupts[INT_SLOT_NUM] = { + {0x0, 0x0, 0, 0x0C}, // Prog timer + {0x0, 0x0, 0, 0x0A}, // Serial interface + {0x0, 0x0, 0, 0x08}, // Input (K10-K13) + {0x0, 0x0, 0, 0x06}, // Input (K00-K03) + {0x0, 0x0, 0, 0x04}, // Stopwatch timer + {0x0, 0x0, 0, 0x02}, // Clock timer +}; + +static breakpoint_t* g_breakpoints = NULL; + +static u32_t call_depth = 0; + +static u32_t clk_timer_timestamp = 0; // in ticks +static u32_t prog_timer_timestamp = 0; // in ticks +static bool_t prog_timer_enabled = 0; +static u8_t prog_timer_data = 0; +static u8_t prog_timer_rld = 0; + +static u32_t tick_counter = 0; +static u32_t ts_freq; +static u8_t speed_ratio = 1; +static timestamp_t ref_ts; + +static state_t cpu_state = { + .pc = &pc, + .x = &x, + .y = &y, + .a = &a, + .b = &b, + .np = &np, + .sp = &sp, + .flags = &flags, + + .tick_counter = &tick_counter, + .clk_timer_timestamp = &clk_timer_timestamp, + .prog_timer_timestamp = &prog_timer_timestamp, + .prog_timer_enabled = &prog_timer_enabled, + .prog_timer_data = &prog_timer_data, + .prog_timer_rld = &prog_timer_rld, + + .call_depth = &call_depth, + + .interrupts = interrupts, + + .memory = memory, +}; + +void cpu_add_bp(breakpoint_t** list, u13_t addr) { + breakpoint_t* bp; + + bp = (breakpoint_t*)g_hal->malloc(sizeof(breakpoint_t)); + if(!bp) { + g_hal->log(LOG_ERROR, "Cannot allocate memory for breakpoint 0x%04X!\n", addr); + return; + } + + bp->addr = addr; + + if(*list != NULL) { + bp->next = *list; + } else { + /* List is empty */ + bp->next = NULL; + } + + *list = bp; +} + +void cpu_free_bp(breakpoint_t** list) { + breakpoint_t *bp = *list, *tmp; + + while(bp != NULL) { + tmp = bp->next; + g_hal->free(bp); + bp = tmp; + } + + *list = NULL; +} + +void cpu_set_speed(u8_t speed) { + speed_ratio = speed; +} + +state_t* cpu_get_state(void) { + return &cpu_state; +} + +u32_t cpu_get_depth(void) { + return call_depth; +} + +static void generate_interrupt(int_slot_t slot, u8_t bit) { + /* Set the factor flag no matter what */ + interrupts[slot].factor_flag_reg = interrupts[slot].factor_flag_reg | (0x1 << bit); + + /* Trigger the INT only if not masked */ + if(interrupts[slot].mask_reg & (0x1 << bit)) { + interrupts[slot].triggered = 1; + } +} + +void cpu_set_input_pin(pin_t pin, pin_state_t state) { + /* Set the I/O */ + inputs[pin & 0x4].states = (inputs[pin & 0x4].states & ~(0x1 << (pin & 0x3))) | + (state << (pin & 0x3)); + + /* Trigger the interrupt (TODO: handle relation register) */ + if(state == PIN_STATE_LOW) { + switch((pin & 0x4) >> 2) { + case 0: + generate_interrupt(INT_K00_K03_SLOT, pin & 0x3); + break; + + case 1: + generate_interrupt(INT_K10_K13_SLOT, pin & 0x3); + break; + } + } +} + +void cpu_sync_ref_timestamp(void) { + ref_ts = g_hal->get_timestamp(); +} + +static u4_t get_io(u12_t n) { + u4_t tmp; + + switch(n) { + case REG_CLK_INT_FACTOR_FLAGS: + /* Interrupt factor flags (clock timer) */ + tmp = interrupts[INT_CLOCK_TIMER_SLOT].factor_flag_reg; + interrupts[INT_CLOCK_TIMER_SLOT].factor_flag_reg = 0; + return tmp; + + case REG_SW_INT_FACTOR_FLAGS: + /* Interrupt factor flags (stopwatch) */ + tmp = interrupts[INT_STOPWATCH_SLOT].factor_flag_reg; + interrupts[INT_STOPWATCH_SLOT].factor_flag_reg = 0; + return tmp; + + case REG_PROG_INT_FACTOR_FLAGS: + /* Interrupt factor flags (prog timer) */ + tmp = interrupts[INT_PROG_TIMER_SLOT].factor_flag_reg; + interrupts[INT_PROG_TIMER_SLOT].factor_flag_reg = 0; + return tmp; + + case REG_SERIAL_INT_FACTOR_FLAGS: + /* Interrupt factor flags (serial) */ + tmp = interrupts[INT_SERIAL_SLOT].factor_flag_reg; + interrupts[INT_SERIAL_SLOT].factor_flag_reg = 0; + return tmp; + + case REG_K00_K03_INT_FACTOR_FLAGS: + /* Interrupt factor flags (K00-K03) */ + tmp = interrupts[INT_K00_K03_SLOT].factor_flag_reg; + interrupts[INT_K00_K03_SLOT].factor_flag_reg = 0; + return tmp; + + case REG_K10_K13_INT_FACTOR_FLAGS: + /* Interrupt factor flags (K10-K13) */ + tmp = interrupts[INT_K10_K13_SLOT].factor_flag_reg; + interrupts[INT_K10_K13_SLOT].factor_flag_reg = 0; + return tmp; + + case REG_CLOCK_INT_MASKS: + /* Clock timer interrupt masks */ + return interrupts[INT_CLOCK_TIMER_SLOT].mask_reg; + + case REG_SW_INT_MASKS: + /* Stopwatch interrupt masks */ + return interrupts[INT_STOPWATCH_SLOT].mask_reg & 0x3; + + case REG_PROG_INT_MASKS: + /* Prog timer interrupt masks */ + return interrupts[INT_PROG_TIMER_SLOT].mask_reg & 0x1; + + case REG_SERIAL_INT_MASKS: + /* Serial interface interrupt masks */ + return interrupts[INT_SERIAL_SLOT].mask_reg & 0x1; + + case REG_K00_K03_INT_MASKS: + /* Input (K00-K03) interrupt masks */ + return interrupts[INT_K00_K03_SLOT].mask_reg; + + case REG_K10_K13_INT_MASKS: + /* Input (K10-K13) interrupt masks */ + return interrupts[INT_K10_K13_SLOT].mask_reg; + + case REG_PROG_TIMER_DATA_L: + /* Prog timer data (low) */ + return prog_timer_data & 0xF; + + case REG_PROG_TIMER_DATA_H: + /* Prog timer data (high) */ + return (prog_timer_data >> 4) & 0xF; + + case REG_PROG_TIMER_RELOAD_DATA_L: + /* Prog timer reload data (low) */ + return prog_timer_rld & 0xF; + + case REG_PROG_TIMER_RELOAD_DATA_H: + /* Prog timer reload data (high) */ + return (prog_timer_rld >> 4) & 0xF; + + case REG_K00_K03_INPUT_PORT: + /* Input port (K00-K03) */ + return inputs[0].states; + + case REG_K10_K13_INPUT_PORT: + /* Input port (K10-K13) */ + return inputs[1].states; + + case REG_K40_K43_BZ_OUTPUT_PORT: + /* Output port (R40-R43) */ + return GET_IO_MEMORY(memory, n); + + case REG_CPU_OSC3_CTRL: + /* CPU/OSC3 clocks switch, CPU voltage switch */ + return GET_IO_MEMORY(memory, n); + + case REG_LCD_CTRL: + /* LCD control */ + return GET_IO_MEMORY(memory, n); + + case REG_LCD_CONTRAST: + /* LCD contrast */ + break; + + case REG_SVD_CTRL: + /* SVD */ + return GET_IO_MEMORY(memory, n) & 0x7; // Voltage always OK + + case REG_BUZZER_CTRL1: + /* Buzzer config 1 */ + return GET_IO_MEMORY(memory, n); + + case REG_BUZZER_CTRL2: + /* Buzzer config 2 */ + return GET_IO_MEMORY(memory, n) & 0x3; // Buzzer ready + + case REG_CLK_WD_TIMER_CTRL: + /* Clock/Watchdog timer reset */ + break; + + case REG_SW_TIMER_CTRL: + /* Stopwatch stop/run/reset */ + break; + + case REG_PROG_TIMER_CTRL: + /* Prog timer stop/run/reset */ + return !!prog_timer_enabled; + + case REG_PROG_TIMER_CLK_SEL: + /* Prog timer clock selection */ + break; + + default: + g_hal->log(LOG_ERROR, "Read from unimplemented I/O 0x%03X - PC = 0x%04X\n", n, pc); + } + + return 0; +} + +static void set_io(u12_t n, u4_t v) { + switch(n) { + case REG_CLOCK_INT_MASKS: + /* Clock timer interrupt masks */ + /* Assume 1Hz timer INT enabled (0x8) */ + interrupts[INT_CLOCK_TIMER_SLOT].mask_reg = v; + break; + + case REG_SW_INT_MASKS: + /* Stopwatch interrupt masks */ + /* Assume all INT disabled */ + interrupts[INT_STOPWATCH_SLOT].mask_reg = v; + break; + + case REG_PROG_INT_MASKS: + /* Prog timer interrupt masks */ + /* Assume Prog timer INT enabled (0x1) */ + interrupts[INT_PROG_TIMER_SLOT].mask_reg = v; + break; + + case REG_SERIAL_INT_MASKS: + /* Serial interface interrupt masks */ + /* Assume all INT disabled */ + interrupts[INT_K10_K13_SLOT].mask_reg = v; + break; + + case REG_K00_K03_INT_MASKS: + /* Input (K00-K03) interrupt masks */ + /* Assume all INT disabled */ + interrupts[INT_SERIAL_SLOT].mask_reg = v; + break; + + case REG_K10_K13_INT_MASKS: + /* Input (K10-K13) interrupt masks */ + /* Assume all INT disabled */ + interrupts[INT_K10_K13_SLOT].mask_reg = v; + break; + + case REG_PROG_TIMER_RELOAD_DATA_L: + /* Prog timer reload data (low) */ + prog_timer_rld = v | (prog_timer_rld & 0xF0); + break; + + case REG_PROG_TIMER_RELOAD_DATA_H: + /* Prog timer reload data (high) */ + prog_timer_rld = (prog_timer_rld & 0xF) | (v << 4); + break; + + case REG_K00_K03_INPUT_PORT: + /* Input port (K00-K03) */ + /* Write not allowed */ + break; + + case REG_K40_K43_BZ_OUTPUT_PORT: + /* Output port (R40-R43) */ + //g_hal->log(LOG_INFO, "Output/Buzzer: 0x%X\n", v); + hw_enable_buzzer(!(v & 0x8)); + break; + + case REG_CPU_OSC3_CTRL: + /* CPU/OSC3 clocks switch, CPU voltage switch */ + /* Assume 32,768 OSC1 selected, OSC3 off, battery >= 3,1V (0x1) */ + break; + + case REG_LCD_CTRL: + /* LCD control */ + break; + + case REG_LCD_CONTRAST: + /* LCD contrast */ + /* Assume medium contrast (0x8) */ + break; + + case REG_SVD_CTRL: + /* SVD */ + /* Assume battery voltage always OK (0x6) */ + break; + + case REG_BUZZER_CTRL1: + /* Buzzer config 1 */ + hw_set_buzzer_freq(v & 0x7); + break; + + case REG_BUZZER_CTRL2: + /* Buzzer config 2 */ + break; + + case REG_CLK_WD_TIMER_CTRL: + /* Clock/Watchdog timer reset */ + /* Ignore watchdog */ + break; + + case REG_SW_TIMER_CTRL: + /* Stopwatch stop/run/reset */ + break; + + case REG_PROG_TIMER_CTRL: + /* Prog timer stop/run/reset */ + if(v & 0x2) { + prog_timer_data = prog_timer_rld; + } + + if((v & 0x1) && !prog_timer_enabled) { + prog_timer_timestamp = tick_counter; + } + + prog_timer_enabled = v & 0x1; + break; + + case REG_PROG_TIMER_CLK_SEL: + /* Prog timer clock selection */ + /* Assume 256Hz, output disabled */ + break; + + default: + g_hal->log(LOG_ERROR, "Write 0x%X to unimplemented I/O 0x%03X - PC = 0x%04X\n", v, n, pc); + } +} + +static void set_lcd(u12_t n, u4_t v) { + u8_t i; + u8_t seg, com0; + + seg = ((n & 0x7F) >> 1); + com0 = (((n & 0x80) >> 7) * 8 + (n & 0x1) * 4); + + for(i = 0; i < 4; i++) { + hw_set_lcd_pin(seg, com0 + i, (v >> i) & 0x1); + } +} + +static u4_t get_memory(u12_t n) { + u4_t res = 0; + + if(n < MEM_RAM_SIZE) { + /* RAM */ + g_hal->log(LOG_MEMORY, "RAM - "); + res = GET_RAM_MEMORY(memory, n); + } else if(n >= MEM_DISPLAY1_ADDR && n < (MEM_DISPLAY1_ADDR + MEM_DISPLAY1_SIZE)) { + /* Display Memory 1 */ + g_hal->log(LOG_MEMORY, "Display Memory 1 - "); + res = GET_DISP1_MEMORY(memory, n); + } else if(n >= MEM_DISPLAY2_ADDR && n < (MEM_DISPLAY2_ADDR + MEM_DISPLAY2_SIZE)) { + /* Display Memory 2 */ + g_hal->log(LOG_MEMORY, "Display Memory 2 - "); + res = GET_DISP2_MEMORY(memory, n); + } else if(n >= MEM_IO_ADDR && n < (MEM_IO_ADDR + MEM_IO_SIZE)) { + /* I/O Memory */ + g_hal->log(LOG_MEMORY, "I/O - "); + res = get_io(n); + } else { + g_hal->log(LOG_ERROR, "Read from invalid memory address 0x%03X - PC = 0x%04X\n", n, pc); + return 0; + } + + g_hal->log(LOG_MEMORY, "Read 0x%X - Address 0x%03X - PC = 0x%04X\n", res, n, pc); + + return res; +} + +static void set_memory(u12_t n, u4_t v) { + /* Cache any data written to a valid address, and process it */ + if(n < MEM_RAM_SIZE) { + /* RAM */ + SET_RAM_MEMORY(memory, n, v); + g_hal->log(LOG_MEMORY, "RAM - "); + } else if(n >= MEM_DISPLAY1_ADDR && n < (MEM_DISPLAY1_ADDR + MEM_DISPLAY1_SIZE)) { + /* Display Memory 1 */ + SET_DISP1_MEMORY(memory, n, v); + set_lcd(n, v); + g_hal->log(LOG_MEMORY, "Display Memory 1 - "); + } else if(n >= MEM_DISPLAY2_ADDR && n < (MEM_DISPLAY2_ADDR + MEM_DISPLAY2_SIZE)) { + /* Display Memory 2 */ + SET_DISP2_MEMORY(memory, n, v); + set_lcd(n, v); + g_hal->log(LOG_MEMORY, "Display Memory 2 - "); + } else if(n >= MEM_IO_ADDR && n < (MEM_IO_ADDR + MEM_IO_SIZE)) { + /* I/O Memory */ + SET_IO_MEMORY(memory, n, v); + set_io(n, v); + g_hal->log(LOG_MEMORY, "I/O - "); + } else { + g_hal->log( + LOG_ERROR, "Write 0x%X to invalid memory address 0x%03X - PC = 0x%04X\n", v, n, pc); + return; + } + + g_hal->log(LOG_MEMORY, "Write 0x%X - Address 0x%03X - PC = 0x%04X\n", v, n, pc); +} + +void cpu_refresh_hw(void) { + static const struct range { + u12_t addr; + u12_t size; + } refresh_locs[] = { + {MEM_DISPLAY1_ADDR, MEM_DISPLAY1_SIZE}, /* Display Memory 1 */ + {MEM_DISPLAY2_ADDR, MEM_DISPLAY2_SIZE}, /* Display Memory 2 */ + {REG_BUZZER_CTRL1, 1}, /* Buzzer frequency */ + {REG_K40_K43_BZ_OUTPUT_PORT, 1}, /* Buzzer enabled */ + + {0, 0}, // end of list + }; + + for(int i = 0; refresh_locs[i].size != 0; i++) { + for(u12_t n = refresh_locs[i].addr; n < (refresh_locs[i].addr + refresh_locs[i].size); + n++) { + set_memory(n, GET_MEMORY(memory, n)); + } + } +} + +static u4_t get_rq(u12_t rq) { + switch(rq & 0x3) { + case 0x0: + return a; + + case 0x1: + return b; + + case 0x2: + return M(x); + + case 0x3: + return M(y); + } + + return 0; +} + +static void set_rq(u12_t rq, u4_t v) { + switch(rq & 0x3) { + case 0x0: + a = v; + break; + + case 0x1: + b = v; + break; + + case 0x2: + SET_M(x, v); + break; + + case 0x3: + SET_M(y, v); + break; + } +} + +/* Instructions */ +static void op_pset_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + np = arg0; +} + +static void op_jp_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + next_pc = arg0 | (np << 8); +} + +static void op_jp_c_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + if(flags & FLAG_C) { + next_pc = arg0 | (np << 8); + } +} + +static void op_jp_nc_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + if(!(flags & FLAG_C)) { + next_pc = arg0 | (np << 8); + } +} + +static void op_jp_z_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + if(flags & FLAG_Z) { + next_pc = arg0 | (np << 8); + } +} + +static void op_jp_nz_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + if(!(flags & FLAG_Z)) { + next_pc = arg0 | (np << 8); + } +} + +static void op_jpba_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + next_pc = a | (b << 4) | (np << 8); +} + +static void op_call_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + pc = (pc + 1) & 0x1FFF; // This does not actually change the PC register + SET_M(sp - 1, PCP); + SET_M(sp - 2, PCSH); + SET_M(sp - 3, PCSL); + sp = (sp - 3) & 0xFF; + next_pc = TO_PC(PCB, NPP, arg0); + call_depth++; +} + +static void op_calz_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + pc = (pc + 1) & 0x1FFF; // This does not actually change the PC register + SET_M(sp - 1, PCP); + SET_M(sp - 2, PCSH); + SET_M(sp - 3, PCSL); + sp = (sp - 3) & 0xFF; + next_pc = TO_PC(PCB, 0, arg0); + call_depth++; +} + +static void op_ret_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + next_pc = M(sp) | (M(sp + 1) << 4) | (M(sp + 2) << 8) | (PCB << 12); + sp = (sp + 3) & 0xFF; + call_depth--; +} + +static void op_rets_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + next_pc = M(sp) | (M(sp + 1) << 4) | (M(sp + 2) << 8) | (PCB << 12); + sp = (sp + 3) & 0xFF; + next_pc = (pc + 1) & 0x1FFF; + call_depth--; +} + +static void op_retd_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + next_pc = M(sp) | (M(sp + 1) << 4) | (M(sp + 2) << 8) | (PCB << 12); + sp = (sp + 3) & 0xFF; + SET_M(x, arg0 & 0xF); + SET_M(x + 1, (arg0 >> 4) & 0xF); + x = ((x + 2) & 0xFF) | (XP << 8); + call_depth--; +} + +static void op_nop5_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); +} + +static void op_nop7_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); +} + +static void op_halt_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + g_hal->halt(); +} + +static void op_inc_x_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + x = ((x + 1) & 0xFF) | (XP << 8); +} + +static void op_inc_y_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + y = ((y + 1) & 0xFF) | (YP << 8); +} + +static void op_ld_x_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + x = arg0 | (XP << 8); +} + +static void op_ld_y_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + y = arg0 | (YP << 8); +} + +static void op_ld_xp_r_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + x = XHL | (RQ(arg0) << 8); +} + +static void op_ld_xh_r_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + x = XL | (RQ(arg0) << 4) | (XP << 8); +} + +static void op_ld_xl_r_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + x = RQ(arg0) | (XH << 4) | (XP << 8); +} + +static void op_ld_yp_r_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + y = YHL | (RQ(arg0) << 8); +} + +static void op_ld_yh_r_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + y = YL | (RQ(arg0) << 4) | (YP << 8); +} + +static void op_ld_yl_r_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + y = RQ(arg0) | (YH << 4) | (YP << 8); +} + +static void op_ld_r_xp_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_RQ(arg0, XP); +} + +static void op_ld_r_xh_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_RQ(arg0, XH); +} + +static void op_ld_r_xl_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_RQ(arg0, XL); +} + +static void op_ld_r_yp_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_RQ(arg0, YP); +} + +static void op_ld_r_yh_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_RQ(arg0, YH); +} + +static void op_ld_r_yl_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_RQ(arg0, YL); +} + +static void op_adc_xh_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + u8_t tmp; + + tmp = XH + arg0 + C; + x = XL | ((tmp & 0xF) << 4) | (XP << 8); + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + if(!(tmp & 0xF)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_adc_xl_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + u8_t tmp; + + tmp = XL + arg0 + C; + x = (tmp & 0xF) | (XH << 4) | (XP << 8); + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + if(!(tmp & 0xF)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_adc_yh_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + u8_t tmp; + + tmp = YH + arg0 + C; + y = YL | ((tmp & 0xF) << 4) | (YP << 8); + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + if(!(tmp & 0xF)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_adc_yl_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + u8_t tmp; + + tmp = YL + arg0 + C; + y = (tmp & 0xF) | (YH << 4) | (YP << 8); + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + if(!(tmp & 0xF)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_cp_xh_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + if(XH < arg0) { + SET_C(); + } else { + CLEAR_C(); + } + if(XH == arg0) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_cp_xl_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + if(XL < arg0) { + SET_C(); + } else { + CLEAR_C(); + } + if(XL == arg0) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_cp_yh_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + if(YH < arg0) { + SET_C(); + } else { + CLEAR_C(); + } + if(YH == arg0) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_cp_yl_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + if(YL < arg0) { + SET_C(); + } else { + CLEAR_C(); + } + if(YL == arg0) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_ld_r_i_cb(u8_t arg0, u8_t arg1) { + SET_RQ(arg0, arg1); +} + +static void op_ld_r_q_cb(u8_t arg0, u8_t arg1) { + SET_RQ(arg0, RQ(arg1)); +} + +static void op_ld_a_mn_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + a = M(arg0); +} + +static void op_ld_b_mn_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + b = M(arg0); +} + +static void op_ld_mn_a_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_M(arg0, a); +} + +static void op_ld_mn_b_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_M(arg0, b); +} + +static void op_ldpx_mx_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_M(x, arg0); + x = ((x + 1) & 0xFF) | (XP << 8); +} + +static void op_ldpx_r_cb(u8_t arg0, u8_t arg1) { + SET_RQ(arg0, RQ(arg1)); + x = ((x + 1) & 0xFF) | (XP << 8); +} + +static void op_ldpy_my_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_M(y, arg0); + y = ((y + 1) & 0xFF) | (YP << 8); +} + +static void op_ldpy_r_cb(u8_t arg0, u8_t arg1) { + SET_RQ(arg0, RQ(arg1)); + y = ((y + 1) & 0xFF) | (YP << 8); +} + +static void op_lbpx_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_M(x, arg0 & 0xF); + SET_M(x + 1, (arg0 >> 4) & 0xF); + x = ((x + 2) & 0xFF) | (XP << 8); +} + +static void op_set_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + flags |= arg0; +} + +static void op_rst_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + flags &= arg0; +} + +static void op_scf_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + SET_C(); +} + +static void op_rcf_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + CLEAR_C(); +} + +static void op_szf_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + SET_Z(); +} + +static void op_rzf_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + CLEAR_Z(); +} + +static void op_sdf_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + SET_D(); +} + +static void op_rdf_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + CLEAR_D(); +} + +static void op_ei_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + SET_I(); +} + +static void op_di_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + CLEAR_I(); +} + +static void op_inc_sp_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + sp = (sp + 1) & 0xFF; +} + +static void op_dec_sp_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + sp = (sp - 1) & 0xFF; +} + +static void op_push_r_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + sp = (sp - 1) & 0xFF; + SET_M(sp, RQ(arg0)); +} + +static void op_push_xp_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + sp = (sp - 1) & 0xFF; + SET_M(sp, XP); +} + +static void op_push_xh_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + sp = (sp - 1) & 0xFF; + SET_M(sp, XH); +} + +static void op_push_xl_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + sp = (sp - 1) & 0xFF; + SET_M(sp, XL); +} + +static void op_push_yp_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + sp = (sp - 1) & 0xFF; + SET_M(sp, YP); +} + +static void op_push_yh_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + sp = (sp - 1) & 0xFF; + SET_M(sp, YH); +} + +static void op_push_yl_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + sp = (sp - 1) & 0xFF; + SET_M(sp, YL); +} + +static void op_push_f_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + sp = (sp - 1) & 0xFF; + SET_M(sp, flags); +} + +static void op_pop_r_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_RQ(arg0, M(sp)); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_xp_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + x = XL | (XH << 4) | (M(sp) << 8); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_xh_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + x = XL | (M(sp) << 4) | (XP << 8); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_xl_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + x = M(sp) | (XH << 4) | (XP << 8); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_yp_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + y = YL | (YH << 4) | (M(sp) << 8); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_yh_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + y = YL | (M(sp) << 4) | (YP << 8); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_yl_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + y = M(sp) | (YH << 4) | (YP << 8); + sp = (sp + 1) & 0xFF; +} + +static void op_pop_f_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg0); + UNUSED(arg1); + flags = M(sp); + sp = (sp + 1) & 0xFF; +} + +static void op_ld_sph_r_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + sp = SPL | (RQ(arg0) << 4); +} + +static void op_ld_spl_r_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + sp = RQ(arg0) | (SPH << 4); +} + +static void op_ld_r_sph_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_RQ(arg0, SPH); +} + +static void op_ld_r_spl_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_RQ(arg0, SPL); +} + +static void op_add_r_i_cb(u8_t arg0, u8_t arg1) { + u8_t tmp; + + tmp = RQ(arg0) + arg1; + if(D) { + if(tmp >= 10) { + SET_RQ(arg0, (tmp - 10) & 0xF); + SET_C(); + } else { + SET_RQ(arg0, tmp); + CLEAR_C(); + } + } else { + SET_RQ(arg0, tmp & 0xF); + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + } + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_add_r_q_cb(u8_t arg0, u8_t arg1) { + u8_t tmp; + + tmp = RQ(arg0) + RQ(arg1); + if(D) { + if(tmp >= 10) { + SET_RQ(arg0, (tmp - 10) & 0xF); + SET_C(); + } else { + SET_RQ(arg0, tmp); + CLEAR_C(); + } + } else { + SET_RQ(arg0, tmp & 0xF); + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + } + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_adc_r_i_cb(u8_t arg0, u8_t arg1) { + u8_t tmp; + + tmp = RQ(arg0) + arg1 + C; + if(D) { + if(tmp >= 10) { + SET_RQ(arg0, (tmp - 10) & 0xF); + SET_C(); + } else { + SET_RQ(arg0, tmp); + CLEAR_C(); + } + } else { + SET_RQ(arg0, tmp & 0xF); + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + } + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_adc_r_q_cb(u8_t arg0, u8_t arg1) { + u8_t tmp; + + tmp = RQ(arg0) + RQ(arg1) + C; + if(D) { + if(tmp >= 10) { + SET_RQ(arg0, (tmp - 10) & 0xF); + SET_C(); + } else { + SET_RQ(arg0, tmp); + CLEAR_C(); + } + } else { + SET_RQ(arg0, tmp & 0xF); + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + } + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_sub_cb(u8_t arg0, u8_t arg1) { + u8_t tmp; + + tmp = RQ(arg0) - RQ(arg1); + if(D) { + if(tmp >> 4) { + SET_RQ(arg0, (tmp - 6) & 0xF); + } else { + SET_RQ(arg0, tmp); + } + } else { + SET_RQ(arg0, tmp & 0xF); + } + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_sbc_r_i_cb(u8_t arg0, u8_t arg1) { + u8_t tmp; + + tmp = RQ(arg0) - arg1 - C; + if(D) { + if(tmp >> 4) { + SET_RQ(arg0, (tmp - 6) & 0xF); + } else { + SET_RQ(arg0, tmp); + } + } else { + SET_RQ(arg0, tmp & 0xF); + } + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_sbc_r_q_cb(u8_t arg0, u8_t arg1) { + u8_t tmp; + + tmp = RQ(arg0) - RQ(arg1) - C; + if(D) { + if(tmp >> 4) { + SET_RQ(arg0, (tmp - 6) & 0xF); + } else { + SET_RQ(arg0, tmp); + } + } else { + SET_RQ(arg0, tmp & 0xF); + } + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_and_r_i_cb(u8_t arg0, u8_t arg1) { + SET_RQ(arg0, RQ(arg0) & arg1); + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_and_r_q_cb(u8_t arg0, u8_t arg1) { + SET_RQ(arg0, RQ(arg0) & RQ(arg1)); + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_or_r_i_cb(u8_t arg0, u8_t arg1) { + SET_RQ(arg0, RQ(arg0) | arg1); + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_or_r_q_cb(u8_t arg0, u8_t arg1) { + SET_RQ(arg0, RQ(arg0) | RQ(arg1)); + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_xor_r_i_cb(u8_t arg0, u8_t arg1) { + SET_RQ(arg0, RQ(arg0) ^ arg1); + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_xor_r_q_cb(u8_t arg0, u8_t arg1) { + SET_RQ(arg0, RQ(arg0) ^ RQ(arg1)); + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_cp_r_i_cb(u8_t arg0, u8_t arg1) { + if(RQ(arg0) < arg1) { + SET_C(); + } else { + CLEAR_C(); + } + if(RQ(arg0) == arg1) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_cp_r_q_cb(u8_t arg0, u8_t arg1) { + if(RQ(arg0) < RQ(arg1)) { + SET_C(); + } else { + CLEAR_C(); + } + if(RQ(arg0) == RQ(arg1)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_fan_r_i_cb(u8_t arg0, u8_t arg1) { + if(!(RQ(arg0) & arg1)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_fan_r_q_cb(u8_t arg0, u8_t arg1) { + if(!(RQ(arg0) & RQ(arg1))) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_rlc_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + u8_t tmp; + + tmp = (RQ(arg0) << 1) | C; + if(RQ(arg0) & 0x8) { + SET_C(); + } else { + CLEAR_C(); + } + SET_RQ(arg0, tmp & 0xF); + /* No need to set Z (issue in DS) */ +} + +static void op_rrc_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + u8_t tmp; + + tmp = (RQ(arg0) >> 1) | (C << 3); + if(RQ(arg0) & 0x1) { + SET_C(); + } else { + CLEAR_C(); + } + SET_RQ(arg0, tmp & 0xF); + /* No need to set Z (issue in DS) */ +} + +static void op_inc_mn_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + u8_t tmp; + + tmp = M(arg0) + 1; + SET_M(arg0, tmp & 0xF); + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + if(!M(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_dec_mn_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + u8_t tmp; + + tmp = M(arg0) - 1; + SET_M(arg0, tmp & 0xF); + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + if(!M(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +static void op_acpx_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + u8_t tmp; + + tmp = M(x) + RQ(arg0) + C; + if(D) { + if(tmp >= 10) { + SET_M(x, (tmp - 10) & 0xF); + SET_C(); + } else { + SET_M(x, tmp); + CLEAR_C(); + } + } else { + SET_M(x, tmp & 0xF); + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + } + if(!M(x)) { + SET_Z(); + } else { + CLEAR_Z(); + } + x = ((x + 1) & 0xFF) | (XP << 8); +} + +static void op_acpy_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + u8_t tmp; + + tmp = M(y) + RQ(arg0) + C; + if(D) { + if(tmp >= 10) { + SET_M(y, (tmp - 10) & 0xF); + SET_C(); + } else { + SET_M(y, tmp); + CLEAR_C(); + } + } else { + SET_M(y, tmp & 0xF); + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + } + if(!M(y)) { + SET_Z(); + } else { + CLEAR_Z(); + } + y = ((y + 1) & 0xFF) | (YP << 8); +} + +static void op_scpx_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + u8_t tmp; + + tmp = M(x) - RQ(arg0) - C; + if(D) { + if(tmp >> 4) { + SET_M(x, (tmp - 6) & 0xF); + } else { + SET_M(x, tmp); + } + } else { + SET_M(x, tmp & 0xF); + } + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + if(!M(x)) { + SET_Z(); + } else { + CLEAR_Z(); + } + x = ((x + 1) & 0xFF) | (XP << 8); +} + +static void op_scpy_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + u8_t tmp; + + tmp = M(y) - RQ(arg0) - C; + if(D) { + if(tmp >> 4) { + SET_M(y, (tmp - 6) & 0xF); + } else { + SET_M(y, tmp); + } + } else { + SET_M(y, tmp & 0xF); + } + if(tmp >> 4) { + SET_C(); + } else { + CLEAR_C(); + } + if(!M(y)) { + SET_Z(); + } else { + CLEAR_Z(); + } + y = ((y + 1) & 0xFF) | (YP << 8); +} + +static void op_not_cb(u8_t arg0, u8_t arg1) { + UNUSED(arg1); + SET_RQ(arg0, ~RQ(arg0) & 0xF); + if(!RQ(arg0)) { + SET_Z(); + } else { + CLEAR_Z(); + } +} + +/* The E0C6S46 supported instructions */ +static const op_t ops[] = { + {"PSET #0x%02X ", 0xE40, MASK_7B, 0, 0, 5, &op_pset_cb}, // PSET + {"JP #0x%02X ", 0x000, MASK_4B, 0, 0, 5, &op_jp_cb}, // JP + {"JP C #0x%02X ", 0x200, MASK_4B, 0, 0, 5, &op_jp_c_cb}, // JP_C + {"JP NC #0x%02X ", 0x300, MASK_4B, 0, 0, 5, &op_jp_nc_cb}, // JP_NC + {"JP Z #0x%02X ", 0x600, MASK_4B, 0, 0, 5, &op_jp_z_cb}, // JP_Z + {"JP NZ #0x%02X ", 0x700, MASK_4B, 0, 0, 5, &op_jp_nz_cb}, // JP_NZ + {"JPBA ", 0xFE8, MASK_12B, 0, 0, 5, &op_jpba_cb}, // JPBA + {"CALL #0x%02X ", 0x400, MASK_4B, 0, 0, 7, &op_call_cb}, // CALL + {"CALZ #0x%02X ", 0x500, MASK_4B, 0, 0, 7, &op_calz_cb}, // CALZ + {"RET ", 0xFDF, MASK_12B, 0, 0, 7, &op_ret_cb}, // RET + {"RETS ", 0xFDE, MASK_12B, 0, 0, 12, &op_rets_cb}, // RETS + {"RETD #0x%02X ", 0x100, MASK_4B, 0, 0, 12, &op_retd_cb}, // RETD + {"NOP5 ", 0xFFB, MASK_12B, 0, 0, 5, &op_nop5_cb}, // NOP5 + {"NOP7 ", 0xFFF, MASK_12B, 0, 0, 7, &op_nop7_cb}, // NOP7 + {"HALT ", 0xFF8, MASK_12B, 0, 0, 5, &op_halt_cb}, // HALT + {"INC X #0x%02X ", 0xEE0, MASK_12B, 0, 0, 5, &op_inc_x_cb}, // INC_X + {"INC Y #0x%02X ", 0xEF0, MASK_12B, 0, 0, 5, &op_inc_y_cb}, // INC_Y + {"LD X #0x%02X ", 0xB00, MASK_4B, 0, 0, 5, &op_ld_x_cb}, // LD_X + {"LD Y #0x%02X ", 0x800, MASK_4B, 0, 0, 5, &op_ld_y_cb}, // LD_Y + {"LD XP R(#0x%02X) ", 0xE80, MASK_10B, 0, 0, 5, &op_ld_xp_r_cb}, // LD_XP_R + {"LD XH R(#0x%02X) ", 0xE84, MASK_10B, 0, 0, 5, &op_ld_xh_r_cb}, // LD_XH_R + {"LD XL R(#0x%02X) ", 0xE88, MASK_10B, 0, 0, 5, &op_ld_xl_r_cb}, // LD_XL_R + {"LD YP R(#0x%02X) ", 0xE90, MASK_10B, 0, 0, 5, &op_ld_yp_r_cb}, // LD_YP_R + {"LD YH R(#0x%02X) ", 0xE94, MASK_10B, 0, 0, 5, &op_ld_yh_r_cb}, // LD_YH_R + {"LD YL R(#0x%02X) ", 0xE98, MASK_10B, 0, 0, 5, &op_ld_yl_r_cb}, // LD_YL_R + {"LD R(#0x%02X) XP ", 0xEA0, MASK_10B, 0, 0, 5, &op_ld_r_xp_cb}, // LD_R_XP + {"LD R(#0x%02X) XH ", 0xEA4, MASK_10B, 0, 0, 5, &op_ld_r_xh_cb}, // LD_R_XH + {"LD R(#0x%02X) XL ", 0xEA8, MASK_10B, 0, 0, 5, &op_ld_r_xl_cb}, // LD_R_XL + {"LD R(#0x%02X) YP ", 0xEB0, MASK_10B, 0, 0, 5, &op_ld_r_yp_cb}, // LD_R_YP + {"LD R(#0x%02X) YH ", 0xEB4, MASK_10B, 0, 0, 5, &op_ld_r_yh_cb}, // LD_R_YH + {"LD R(#0x%02X) YL ", 0xEB8, MASK_10B, 0, 0, 5, &op_ld_r_yl_cb}, // LD_R_YL + {"ADC XH #0x%02X ", 0xA00, MASK_8B, 0, 0, 7, &op_adc_xh_cb}, // ADC_XH + {"ADC XL #0x%02X ", 0xA10, MASK_8B, 0, 0, 7, &op_adc_xl_cb}, // ADC_XL + {"ADC YH #0x%02X ", 0xA20, MASK_8B, 0, 0, 7, &op_adc_yh_cb}, // ADC_YH + {"ADC YL #0x%02X ", 0xA30, MASK_8B, 0, 0, 7, &op_adc_yl_cb}, // ADC_YL + {"CP XH #0x%02X ", 0xA40, MASK_8B, 0, 0, 7, &op_cp_xh_cb}, // CP_XH + {"CP XL #0x%02X ", 0xA50, MASK_8B, 0, 0, 7, &op_cp_xl_cb}, // CP_XL + {"CP YH #0x%02X ", 0xA60, MASK_8B, 0, 0, 7, &op_cp_yh_cb}, // CP_YH + {"CP YL #0x%02X ", 0xA70, MASK_8B, 0, 0, 7, &op_cp_yl_cb}, // CP_YL + {"LD R(#0x%02X) #0x%02X ", 0xE00, MASK_6B, 4, 0x030, 5, &op_ld_r_i_cb}, // LD_R_I + {"LD R(#0x%02X) Q(#0x%02X)", 0xEC0, MASK_8B, 2, 0x00C, 5, &op_ld_r_q_cb}, // LD_R_Q + {"LD A M(#0x%02X) ", 0xFA0, MASK_8B, 0, 0, 5, &op_ld_a_mn_cb}, // LD_A_MN + {"LD B M(#0x%02X) ", 0xFB0, MASK_8B, 0, 0, 5, &op_ld_b_mn_cb}, // LD_B_MN + {"LD M(#0x%02X) A ", 0xF80, MASK_8B, 0, 0, 5, &op_ld_mn_a_cb}, // LD_MN_A + {"LD M(#0x%02X) B ", 0xF90, MASK_8B, 0, 0, 5, &op_ld_mn_b_cb}, // LD_MN_B + {"LDPX MX #0x%02X ", 0xE60, MASK_8B, 0, 0, 5, &op_ldpx_mx_cb}, // LDPX_MX + {"LDPX R(#0x%02X) Q(#0x%02X)", 0xEE0, MASK_8B, 2, 0x00C, 5, &op_ldpx_r_cb}, // LDPX_R + {"LDPY MY #0x%02X ", 0xE70, MASK_8B, 0, 0, 5, &op_ldpy_my_cb}, // LDPY_MY + {"LDPY R(#0x%02X) Q(#0x%02X)", 0xEF0, MASK_8B, 2, 0x00C, 5, &op_ldpy_r_cb}, // LDPY_R + {"LBPX #0x%02X ", 0x900, MASK_4B, 0, 0, 5, &op_lbpx_cb}, // LBPX + {"SET #0x%02X ", 0xF40, MASK_8B, 0, 0, 7, &op_set_cb}, // SET + {"RST #0x%02X ", 0xF50, MASK_8B, 0, 0, 7, &op_rst_cb}, // RST + {"SCF ", 0xF41, MASK_12B, 0, 0, 7, &op_scf_cb}, // SCF + {"RCF ", 0xF5E, MASK_12B, 0, 0, 7, &op_rcf_cb}, // RCF + {"SZF ", 0xF42, MASK_12B, 0, 0, 7, &op_szf_cb}, // SZF + {"RZF ", 0xF5D, MASK_12B, 0, 0, 7, &op_rzf_cb}, // RZF + {"SDF ", 0xF44, MASK_12B, 0, 0, 7, &op_sdf_cb}, // SDF + {"RDF ", 0xF5B, MASK_12B, 0, 0, 7, &op_rdf_cb}, // RDF + {"EI ", 0xF48, MASK_12B, 0, 0, 7, &op_ei_cb}, // EI + {"DI ", 0xF57, MASK_12B, 0, 0, 7, &op_di_cb}, // DI + {"INC SP ", 0xFDB, MASK_12B, 0, 0, 5, &op_inc_sp_cb}, // INC_SP + {"DEC SP ", 0xFCB, MASK_12B, 0, 0, 5, &op_dec_sp_cb}, // DEC_SP + {"PUSH R(#0x%02X) ", 0xFC0, MASK_10B, 0, 0, 5, &op_push_r_cb}, // PUSH_R + {"PUSH XP ", 0xFC4, MASK_12B, 0, 0, 5, &op_push_xp_cb}, // PUSH_XP + {"PUSH XH ", 0xFC5, MASK_12B, 0, 0, 5, &op_push_xh_cb}, // PUSH_XH + {"PUSH XL ", 0xFC6, MASK_12B, 0, 0, 5, &op_push_xl_cb}, // PUSH_XL + {"PUSH YP ", 0xFC7, MASK_12B, 0, 0, 5, &op_push_yp_cb}, // PUSH_YP + {"PUSH YH ", 0xFC8, MASK_12B, 0, 0, 5, &op_push_yh_cb}, // PUSH_YH + {"PUSH YL ", 0xFC9, MASK_12B, 0, 0, 5, &op_push_yl_cb}, // PUSH_YL + {"PUSH F ", 0xFCA, MASK_12B, 0, 0, 5, &op_push_f_cb}, // PUSH_F + {"POP R(#0x%02X) ", 0xFD0, MASK_10B, 0, 0, 5, &op_pop_r_cb}, // POP_R + {"POP XP ", 0xFD4, MASK_12B, 0, 0, 5, &op_pop_xp_cb}, // POP_XP + {"POP XH ", 0xFD5, MASK_12B, 0, 0, 5, &op_pop_xh_cb}, // POP_XH + {"POP XL ", 0xFD6, MASK_12B, 0, 0, 5, &op_pop_xl_cb}, // POP_XL + {"POP YP ", 0xFD7, MASK_12B, 0, 0, 5, &op_pop_yp_cb}, // POP_YP + {"POP YH ", 0xFD8, MASK_12B, 0, 0, 5, &op_pop_yh_cb}, // POP_YH + {"POP YL ", 0xFD9, MASK_12B, 0, 0, 5, &op_pop_yl_cb}, // POP_YL + {"POP F ", 0xFDA, MASK_12B, 0, 0, 5, &op_pop_f_cb}, // POP_F + {"LD SPH R(#0x%02X) ", 0xFE0, MASK_10B, 0, 0, 5, &op_ld_sph_r_cb}, // LD_SPH_R + {"LD SPL R(#0x%02X) ", 0xFF0, MASK_10B, 0, 0, 5, &op_ld_spl_r_cb}, // LD_SPL_R + {"LD R(#0x%02X) SPH ", 0xFE4, MASK_10B, 0, 0, 5, &op_ld_r_sph_cb}, // LD_R_SPH + {"LD R(#0x%02X) SPL ", 0xFF4, MASK_10B, 0, 0, 5, &op_ld_r_spl_cb}, // LD_R_SPL + {"ADD R(#0x%02X) #0x%02X ", 0xC00, MASK_6B, 4, 0x030, 7, &op_add_r_i_cb}, // ADD_R_I + {"ADD R(#0x%02X) Q(#0x%02X)", 0xA80, MASK_8B, 2, 0x00C, 7, &op_add_r_q_cb}, // ADD_R_Q + {"ADC R(#0x%02X) #0x%02X ", 0xC40, MASK_6B, 4, 0x030, 7, &op_adc_r_i_cb}, // ADC_R_I + {"ADC R(#0x%02X) Q(#0x%02X)", 0xA90, MASK_8B, 2, 0x00C, 7, &op_adc_r_q_cb}, // ADC_R_Q + {"SUB R(#0x%02X) Q(#0x%02X)", 0xAA0, MASK_8B, 2, 0x00C, 7, &op_sub_cb}, // SUB + {"SBC R(#0x%02X) #0x%02X ", 0xB40, MASK_6B, 4, 0x030, 7, &op_sbc_r_i_cb}, // SBC_R_I + {"SBC R(#0x%02X) Q(#0x%02X)", 0xAB0, MASK_8B, 2, 0x00C, 7, &op_sbc_r_q_cb}, // SBC_R_Q + {"AND R(#0x%02X) #0x%02X ", 0xC80, MASK_6B, 4, 0x030, 7, &op_and_r_i_cb}, // AND_R_I + {"AND R(#0x%02X) Q(#0x%02X)", 0xAC0, MASK_8B, 2, 0x00C, 7, &op_and_r_q_cb}, // AND_R_Q + {"OR R(#0x%02X) #0x%02X ", 0xCC0, MASK_6B, 4, 0x030, 7, &op_or_r_i_cb}, // OR_R_I + {"OR R(#0x%02X) Q(#0x%02X)", 0xAD0, MASK_8B, 2, 0x00C, 7, &op_or_r_q_cb}, // OR_R_Q + {"XOR R(#0x%02X) #0x%02X ", 0xD00, MASK_6B, 4, 0x030, 7, &op_xor_r_i_cb}, // XOR_R_I + {"XOR R(#0x%02X) Q(#0x%02X)", 0xAE0, MASK_8B, 2, 0x00C, 7, &op_xor_r_q_cb}, // XOR_R_Q + {"CP R(#0x%02X) #0x%02X ", 0xDC0, MASK_6B, 4, 0x030, 7, &op_cp_r_i_cb}, // CP_R_I + {"CP R(#0x%02X) Q(#0x%02X)", 0xF00, MASK_8B, 2, 0x00C, 7, &op_cp_r_q_cb}, // CP_R_Q + {"FAN R(#0x%02X) #0x%02X ", 0xD80, MASK_6B, 4, 0x030, 7, &op_fan_r_i_cb}, // FAN_R_I + {"FAN R(#0x%02X) Q(#0x%02X)", 0xF10, MASK_8B, 2, 0x00C, 7, &op_fan_r_q_cb}, // FAN_R_Q + {"RLC R(#0x%02X) ", 0xAF0, MASK_8B, 0, 0, 7, &op_rlc_cb}, // RLC + {"RRC R(#0x%02X) ", 0xE8C, MASK_10B, 0, 0, 5, &op_rrc_cb}, // RRC + {"INC M(#0x%02X) ", 0xF60, MASK_8B, 0, 0, 7, &op_inc_mn_cb}, // INC_MN + {"DEC M(#0x%02X) ", 0xF70, MASK_8B, 0, 0, 7, &op_dec_mn_cb}, // DEC_MN + {"ACPX R(#0x%02X) ", 0xF28, MASK_10B, 0, 0, 7, &op_acpx_cb}, // ACPX + {"ACPY R(#0x%02X) ", 0xF2C, MASK_10B, 0, 0, 7, &op_acpy_cb}, // ACPY + {"SCPX R(#0x%02X) ", 0xF38, MASK_10B, 0, 0, 7, &op_scpx_cb}, // SCPX + {"SCPY R(#0x%02X) ", 0xF3C, MASK_10B, 0, 0, 7, &op_scpy_cb}, // SCPY + {"NOT R(#0x%02X) ", 0xD0F, 0xFCF, 4, 0, 7, &op_not_cb}, // NOT + + {NULL, 0, 0, 0, 0, 0, NULL}, +}; + +static timestamp_t wait_for_cycles(timestamp_t since, u8_t cycles) { + timestamp_t deadline; + + tick_counter += cycles; + + if(speed_ratio == 0) { + /* Emulation will be as fast as possible */ + return g_hal->get_timestamp(); + } + + deadline = since + (cycles * ts_freq) / (TICK_FREQUENCY * speed_ratio); + g_hal->sleep_until(deadline); + + return deadline; +} + +static void process_interrupts(void) { + u8_t i; + + /* Process interrupts in priority order */ + for(i = 0; i < INT_SLOT_NUM; i++) { + if(interrupts[i].triggered) { + //printf("IT %u !\n", i); + SET_M(sp - 1, PCP); + SET_M(sp - 2, PCSH); + SET_M(sp - 3, PCSL); + sp = (sp - 3) & 0xFF; + CLEAR_I(); + np = TO_NP(NBP, 1); + pc = TO_PC(PCB, 1, interrupts[i].vector); + call_depth++; + + ref_ts = wait_for_cycles(ref_ts, 12); + interrupts[i].triggered = 0; + } + } +} + +static void print_state(u8_t op_num, u12_t op, u13_t addr) { + u8_t i; + + if(!g_hal->is_log_enabled(LOG_CPU)) { + return; + } + + g_hal->log(LOG_CPU, "0x%04X: ", addr); + + for(i = 0; i < call_depth; i++) { + g_hal->log(LOG_CPU, " "); + } + + if(ops[op_num].mask_arg0 != 0) { + /* Two arguments */ + g_hal->log( + LOG_CPU, + ops[op_num].log, + (op & ops[op_num].mask_arg0) >> ops[op_num].shift_arg0, + op & ~(ops[op_num].mask | ops[op_num].mask_arg0)); + } else { + /* One argument */ + g_hal->log(LOG_CPU, ops[op_num].log, (op & ~ops[op_num].mask) >> ops[op_num].shift_arg0); + } + + if(call_depth < 10) { + for(i = 0; i < (10 - call_depth); i++) { + g_hal->log(LOG_CPU, " "); + } + } + + g_hal->log(LOG_CPU, " ; 0x%03X - ", op); + for(i = 0; i < 12; i++) { + g_hal->log(LOG_CPU, "%s", ((op >> (11 - i)) & 0x1) ? "1" : "0"); + } + g_hal->log( + LOG_CPU, + " - PC = 0x%04X, SP = 0x%02X, NP = 0x%02X, X = 0x%03X, Y = 0x%03X, A = 0x%X, B = 0x%X, F = 0x%X\n", + pc, + sp, + np, + x, + y, + a, + b, + flags); +} + +void cpu_reset(void) { + u13_t i; + + /* Registers and variables init */ + pc = TO_PC(0, 1, 0x00); // PC starts at bank 0, page 1, step 0 + np = TO_NP(0, 1); // NP starts at page 1 + a = 0; // undef + b = 0; // undef + x = 0; // undef + y = 0; // undef + sp = 0; // undef + flags = 0; + + /* Init RAM to zeros */ + for(i = 0; i < MEM_BUFFER_SIZE; i++) { + memory[i] = 0; + } + + SET_IO_MEMORY(memory, REG_K40_K43_BZ_OUTPUT_PORT, 0xF); // Output port (R40-R43) + SET_IO_MEMORY(memory, REG_LCD_CTRL, 0x8); // LCD control + /* TODO: Input relation register */ + + cpu_sync_ref_timestamp(); +} + +bool_t cpu_init(const u12_t* program, breakpoint_t* breakpoints, u32_t freq) { + g_program = program; + g_breakpoints = breakpoints; + ts_freq = freq; + + cpu_reset(); + + return 0; +} + +void cpu_release(void) { +} + +int cpu_step(void) { + u12_t op; + u8_t i; + breakpoint_t* bp = g_breakpoints; + static u8_t previous_cycles = 0; + + op = g_program[pc]; + + /* Lookup the OP code */ + for(i = 0; ops[i].log != NULL; i++) { + if((op & ops[i].mask) == ops[i].code) { + break; + } + } + + if(ops[i].log == NULL) { + g_hal->log(LOG_ERROR, "Unknown op-code 0x%X (pc = 0x%04X)\n", op, pc); + return 1; + } + + next_pc = (pc + 1) & 0x1FFF; + + /* Display the operation along with the current state of the processor */ + print_state(i, op, pc); + + /* Match the speed of the real processor + * NOTE: For better accuracy, the final wait should happen here, however + * the downside is that all interrupts will likely be delayed by one OP + */ + ref_ts = wait_for_cycles(ref_ts, previous_cycles); + + /* Process the OP code */ + if(ops[i].cb != NULL) { + if(ops[i].mask_arg0 != 0) { + /* Two arguments */ + ops[i].cb( + (op & ops[i].mask_arg0) >> ops[i].shift_arg0, + op & ~(ops[i].mask | ops[i].mask_arg0)); + } else { + /* One arguments */ + ops[i].cb((op & ~ops[i].mask) >> ops[i].shift_arg0, 0); + } + } + + /* Prepare for the next instruction */ + pc = next_pc; + previous_cycles = ops[i].cycles; + + if(i > 0) { + /* OP code is not PSET, reset NP */ + np = (pc >> 8) & 0x1F; + } + + /* Handle timers using the internal tick counter */ + if(tick_counter - clk_timer_timestamp >= TIMER_1HZ_PERIOD) { + do { + clk_timer_timestamp += TIMER_1HZ_PERIOD; + } while(tick_counter - clk_timer_timestamp >= TIMER_1HZ_PERIOD); + + generate_interrupt(INT_CLOCK_TIMER_SLOT, 3); + } + + if(prog_timer_enabled && tick_counter - prog_timer_timestamp >= TIMER_256HZ_PERIOD) { + do { + prog_timer_timestamp += TIMER_256HZ_PERIOD; + prog_timer_data--; + + if(prog_timer_data == 0) { + prog_timer_data = prog_timer_rld; + generate_interrupt(INT_PROG_TIMER_SLOT, 0); + } + } while(tick_counter - prog_timer_timestamp >= TIMER_256HZ_PERIOD); + } + + /* Check if there is any pending interrupt */ + if(I && i > 0) { // Do not process interrupts after a PSET operation + process_interrupts(); + } + + /* Check if we could pause the execution */ + while(bp != NULL) { + if(bp->addr == pc) { + return 1; + } + + bp = bp->next; + } + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/cpu.h b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/cpu.h new file mode 100644 index 000000000..4405103b2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/cpu.h @@ -0,0 +1,215 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _CPU_H_ +#define _CPU_H_ + +#include "hal.h" + +#define MEMORY_SIZE 4096 // 4096 x 4 bits (640 x 4 bits of RAM) + +#define MEM_RAM_ADDR 0x000 +#define MEM_RAM_SIZE 0x280 +#define MEM_DISPLAY1_ADDR 0xE00 +#define MEM_DISPLAY1_SIZE 0x050 +#define MEM_DISPLAY2_ADDR 0xE80 +#define MEM_DISPLAY2_SIZE 0x050 +#define MEM_IO_ADDR 0xF00 +#define MEM_IO_SIZE 0x080 + +/* Define this if you want to reduce the footprint of the memory buffer from 4096 u4_t (most likely bytes) + * to 464 u8_t (bytes for sure), while increasing slightly the number of operations needed to read/write from/to it. + */ +#define LOW_FOOTPRINT + +#ifdef LOW_FOOTPRINT +/* Invalid memory areas are not buffered to reduce the footprint of the library in memory */ +#define MEM_BUFFER_SIZE (MEM_RAM_SIZE + MEM_DISPLAY1_SIZE + MEM_DISPLAY2_SIZE + MEM_IO_SIZE) / 2 + +/* Maps the CPU memory to the memory buffer */ +#define RAM_TO_MEMORY(n) ((n - MEM_RAM_ADDR) / 2) +#define DISP1_TO_MEMORY(n) ((n - MEM_DISPLAY1_ADDR + MEM_RAM_SIZE) / 2) +#define DISP2_TO_MEMORY(n) ((n - MEM_DISPLAY2_ADDR + MEM_RAM_SIZE + MEM_DISPLAY1_SIZE) / 2) +#define IO_TO_MEMORY(n) \ + ((n - MEM_IO_ADDR + MEM_RAM_SIZE + MEM_DISPLAY1_SIZE + MEM_DISPLAY2_SIZE) / 2) + +#define SET_RAM_MEMORY(buffer, n, v) \ + { \ + buffer[RAM_TO_MEMORY(n)] = (buffer[RAM_TO_MEMORY(n)] & ~(0xF << (((n) % 2) << 2))) | \ + ((v)&0xF) << (((n) % 2) << 2); \ + } +#define SET_DISP1_MEMORY(buffer, n, v) \ + { \ + buffer[DISP1_TO_MEMORY(n)] = (buffer[DISP1_TO_MEMORY(n)] & ~(0xF << (((n) % 2) << 2))) | \ + ((v)&0xF) << (((n) % 2) << 2); \ + } +#define SET_DISP2_MEMORY(buffer, n, v) \ + { \ + buffer[DISP2_TO_MEMORY(n)] = (buffer[DISP2_TO_MEMORY(n)] & ~(0xF << (((n) % 2) << 2))) | \ + ((v)&0xF) << (((n) % 2) << 2); \ + } +#define SET_IO_MEMORY(buffer, n, v) \ + { \ + buffer[IO_TO_MEMORY(n)] = (buffer[IO_TO_MEMORY(n)] & ~(0xF << (((n) % 2) << 2))) | \ + ((v)&0xF) << (((n) % 2) << 2); \ + } +#define SET_MEMORY(buffer, n, v) \ + { \ + if((n) < (MEM_RAM_ADDR + MEM_RAM_SIZE)) { \ + SET_RAM_MEMORY(buffer, n, v); \ + } else if((n) < MEM_DISPLAY1_ADDR) { \ + /* INVALID_MEMORY */ \ + } else if((n) < (MEM_DISPLAY1_ADDR + MEM_DISPLAY1_SIZE)) { \ + SET_DISP1_MEMORY(buffer, n, v); \ + } else if((n) < MEM_DISPLAY2_ADDR) { \ + /* INVALID_MEMORY */ \ + } else if((n) < (MEM_DISPLAY2_ADDR + MEM_DISPLAY2_SIZE)) { \ + SET_DISP2_MEMORY(buffer, n, v); \ + } else if((n) < MEM_IO_ADDR) { \ + /* INVALID_MEMORY */ \ + } else if((n) < (MEM_IO_ADDR + MEM_IO_SIZE)) { \ + SET_IO_MEMORY(buffer, n, v); \ + } else { \ + /* INVALID_MEMORY */ \ + } \ + } + +#define GET_RAM_MEMORY(buffer, n) ((buffer[RAM_TO_MEMORY(n)] >> (((n) % 2) << 2)) & 0xF) +#define GET_DISP1_MEMORY(buffer, n) ((buffer[DISP1_TO_MEMORY(n)] >> (((n) % 2) << 2)) & 0xF) +#define GET_DISP2_MEMORY(buffer, n) ((buffer[DISP2_TO_MEMORY(n)] >> (((n) % 2) << 2)) & 0xF) +#define GET_IO_MEMORY(buffer, n) ((buffer[IO_TO_MEMORY(n)] >> (((n) % 2) << 2)) & 0xF) +#define GET_MEMORY(buffer, n) \ + ((buffer \ + [((n) < (MEM_RAM_ADDR + MEM_RAM_SIZE)) ? RAM_TO_MEMORY(n) : \ + ((n) < MEM_DISPLAY1_ADDR) ? 0 : \ + ((n) < (MEM_DISPLAY1_ADDR + MEM_DISPLAY1_SIZE)) ? DISP1_TO_MEMORY(n) : \ + ((n) < MEM_DISPLAY2_ADDR) ? 0 : \ + ((n) < (MEM_DISPLAY2_ADDR + MEM_DISPLAY2_SIZE)) ? DISP2_TO_MEMORY(n) : \ + ((n) < MEM_IO_ADDR) ? 0 : \ + ((n) < (MEM_IO_ADDR + MEM_IO_SIZE)) ? IO_TO_MEMORY(n) : \ + 0] >> \ + (((n) % 2) << 2)) & \ + 0xF) + +#define MEM_BUFFER_TYPE u8_t +#else +#define MEM_BUFFER_SIZE MEMORY_SIZE + +#define SET_MEMORY(buffer, n, v) \ + { buffer[n] = v; } +#define SET_RAM_MEMORY(buffer, n, v) SET_MEMORY(buffer, n, v) +#define SET_DISP1_MEMORY(buffer, n, v) SET_MEMORY(buffer, n, v) +#define SET_DISP2_MEMORY(buffer, n, v) SET_MEMORY(buffer, n, v) +#define SET_IO_MEMORY(buffer, n, v) SET_MEMORY(buffer, n, v) + +#define GET_MEMORY(buffer, n) (buffer[n]) +#define GET_RAM_MEMORY(buffer, n) GET_MEMORY(buffer, n) +#define GET_DISP1_MEMORY(buffer, n) GET_MEMORY(buffer, n) +#define GET_DISP2_MEMORY(buffer, n) GET_MEMORY(buffer, n) +#define GET_IO_MEMORY(buffer, n) GET_MEMORY(buffer, n) + +#define MEM_BUFFER_TYPE u4_t +#endif + +typedef struct breakpoint { + u13_t addr; + struct breakpoint* next; +} breakpoint_t; + +/* Pins (TODO: add other pins) */ +typedef enum { + PIN_K00 = 0x0, + PIN_K01 = 0x1, + PIN_K02 = 0x2, + PIN_K03 = 0x3, + PIN_K10 = 0X4, + PIN_K11 = 0X5, + PIN_K12 = 0X6, + PIN_K13 = 0X7, +} pin_t; + +typedef enum { + PIN_STATE_LOW = 0, + PIN_STATE_HIGH = 1, +} pin_state_t; + +typedef enum { + INT_PROG_TIMER_SLOT = 0, + INT_SERIAL_SLOT = 1, + INT_K10_K13_SLOT = 2, + INT_K00_K03_SLOT = 3, + INT_STOPWATCH_SLOT = 4, + INT_CLOCK_TIMER_SLOT = 5, + INT_SLOT_NUM, +} int_slot_t; + +typedef struct { + u4_t factor_flag_reg; + u4_t mask_reg; + bool_t triggered; /* 1 if triggered, 0 otherwise */ + u8_t vector; +} interrupt_t; + +typedef struct { + u13_t* pc; + u12_t* x; + u12_t* y; + u4_t* a; + u4_t* b; + u5_t* np; + u8_t* sp; + u4_t* flags; + + u32_t* tick_counter; + u32_t* clk_timer_timestamp; + u32_t* prog_timer_timestamp; + bool_t* prog_timer_enabled; + u8_t* prog_timer_data; + u8_t* prog_timer_rld; + + u32_t* call_depth; + + interrupt_t* interrupts; + + MEM_BUFFER_TYPE* memory; +} state_t; + +void cpu_add_bp(breakpoint_t** list, u13_t addr); +void cpu_free_bp(breakpoint_t** list); + +void cpu_set_speed(u8_t speed); + +state_t* cpu_get_state(void); + +u32_t cpu_get_depth(void); + +void cpu_set_input_pin(pin_t pin, pin_state_t state); + +void cpu_sync_ref_timestamp(void); + +void cpu_refresh_hw(void); + +void cpu_reset(void); + +bool_t cpu_init(const u12_t* program, breakpoint_t* breakpoints, u32_t freq); +void cpu_release(void); + +int cpu_step(void); + +#endif /* _CPU_H_ */ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/hal.h b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/hal.h new file mode 100644 index 000000000..84140befb --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/hal.h @@ -0,0 +1,89 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _HAL_H_ +#define _HAL_H_ + +#include "../hal_types.h" + +#ifndef NULL +#define NULL 0 +#endif + +typedef enum { + LOG_ERROR = 0x1, + LOG_INFO = (0x1 << 1), + LOG_MEMORY = (0x1 << 2), + LOG_CPU = (0x1 << 3), +} log_level_t; + +/* The Hardware Abstraction Layer + * NOTE: This structure acts as an abstraction layer between TamaLIB and the OS/SDK. + * All pointers MUST be implemented, but some implementations can be left empty. + */ +typedef struct { + /* Memory allocation functions + * NOTE: Needed only if breakpoints support is required. + */ + void* (*malloc)(u32_t size); + void (*free)(void* ptr); + + /* What to do if the CPU has halted + */ + void (*halt)(void); + + /* Log related function + * NOTE: Needed only if log messages are required. + */ + bool_t (*is_log_enabled)(log_level_t level); + void (*log)(log_level_t level, char* buff, ...); + + /* Clock related functions + * NOTE: Timestamps granularity is configured with tamalib_init(), an accuracy + * of ~30 us (1/32768) is required for a cycle accurate emulation. + */ + void (*sleep_until)(timestamp_t ts); + timestamp_t (*get_timestamp)(void); + + /* Screen related functions + * NOTE: In case of direct hardware access to pixels, the set_XXXX() functions + * (called for each pixel/icon update) can directly drive them, otherwise they + * should just store the data in a buffer and let update_screen() do the actual + * rendering (at 30 fps). + */ + void (*update_screen)(void); + void (*set_lcd_matrix)(u8_t x, u8_t y, bool_t val); + void (*set_lcd_icon)(u8_t icon, bool_t val); + + /* Sound related functions + * NOTE: set_frequency() changes the output frequency of the sound, while + * play_frequency() decides whether the sound should be heard or not. + */ + void (*set_frequency)(u32_t freq); + void (*play_frequency)(bool_t en); + + /* Event handler from the main app (if any) + * NOTE: This function usually handles button related events, states loading/saving ... + */ + int (*handler)(void); +} hal_t; + +extern hal_t* g_hal; + +#endif /* _HAL_H_ */ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/hal_types.h.template b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/hal_types.h.template new file mode 100644 index 000000000..38c4d212c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/hal_types.h.template @@ -0,0 +1,32 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _HAL_TYPES_H_ +#define _HAL_TYPES_H_ + +typedef unsigned char bool_t; +typedef unsigned char u4_t; +typedef unsigned char u5_t; +typedef unsigned char u8_t; +typedef unsigned short u12_t; +typedef unsigned short u13_t; +typedef unsigned int u32_t; +typedef unsigned int timestamp_t; // WARNING: Must be an unsigned type to properly handle wrapping (u32 wraps in around 1h11m when expressed in us) + +#endif /* _HAL_TYPES_H_ */ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/hw.c b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/hw.c new file mode 100644 index 000000000..0a91f2a78 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/hw.c @@ -0,0 +1,134 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "hw.h" +#include "cpu.h" +#include "hal.h" + +/* SEG -> LCD mapping */ +static u8_t seg_pos[40] = {0, 1, 2, 3, 4, 5, 6, 7, 32, 8, 9, 10, 11, 12, + 13, 14, 15, 33, 34, 35, 31, 30, 29, 28, 27, 26, 25, 24, + 36, 23, 22, 21, 20, 19, 18, 17, 16, 37, 38, 39}; + +bool_t hw_init(void) { + /* Buttons are active LOW */ + cpu_set_input_pin(PIN_K00, PIN_STATE_HIGH); + cpu_set_input_pin(PIN_K01, PIN_STATE_HIGH); + cpu_set_input_pin(PIN_K02, PIN_STATE_HIGH); + + return 0; +} + +void hw_release(void) { +} + +void hw_set_lcd_pin(u8_t seg, u8_t com, u8_t val) { + if(seg_pos[seg] < LCD_WIDTH) { + g_hal->set_lcd_matrix(seg_pos[seg], com, val); + } else { + /* + * IC n -> seg-com|... + * IC 0 -> 8-0 |18-3 |19-2 + * IC 1 -> 8-1 |17-0 |19-3 + * IC 2 -> 8-2 |17-1 |37-12|38-13|39-14 + * IC 3 -> 8-3 |17-2 |18-1 |19-0 + * IC 4 -> 28-12|37-13|38-14|39-15 + * IC 5 -> 28-13|37-14|38-15 + * IC 6 -> 28-14|37-15|39-12 + * IC 7 -> 28-15|38-12|39-13 + */ + if(seg == 8 && com < 4) { + g_hal->set_lcd_icon(com, val); + } else if(seg == 28 && com >= 12) { + g_hal->set_lcd_icon(com - 8, val); + } + } +} + +void hw_set_button(button_t btn, btn_state_t state) { + pin_state_t pin_state = (state == BTN_STATE_PRESSED) ? PIN_STATE_LOW : PIN_STATE_HIGH; + + switch(btn) { + case BTN_LEFT: + cpu_set_input_pin(PIN_K02, pin_state); + break; + + case BTN_MIDDLE: + cpu_set_input_pin(PIN_K01, pin_state); + break; + + case BTN_RIGHT: + cpu_set_input_pin(PIN_K00, pin_state); + break; + } +} + +void hw_set_buzzer_freq(u4_t freq) { + u32_t snd_freq = 0; + + switch(freq) { + case 0: + /* 4096.0 Hz */ + snd_freq = 40960; + break; + + case 1: + /* 3276.8 Hz */ + snd_freq = 32768; + break; + + case 2: + /* 2730.7 Hz */ + snd_freq = 27307; + break; + + case 3: + /* 2340.6 Hz */ + snd_freq = 23406; + break; + + case 4: + /* 2048.0 Hz */ + snd_freq = 20480; + break; + + case 5: + /* 1638.4 Hz */ + snd_freq = 16384; + break; + + case 6: + /* 1365.3 Hz */ + snd_freq = 13653; + break; + + case 7: + /* 1170.3 Hz */ + snd_freq = 11703; + break; + } + + if(snd_freq != 0) { + g_hal->set_frequency(snd_freq); + } +} + +void hw_enable_buzzer(bool_t en) { + g_hal->play_frequency(en); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/hw.h b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/hw.h new file mode 100644 index 000000000..853515b39 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/hw.h @@ -0,0 +1,50 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _HW_H_ +#define _HW_H_ + +#include "hal.h" + +#define LCD_WIDTH 32 +#define LCD_HEIGHT 16 + +#define ICON_NUM 8 + +typedef enum { + BTN_STATE_RELEASED = 0, + BTN_STATE_PRESSED, +} btn_state_t; + +typedef enum { + BTN_LEFT = 0, + BTN_MIDDLE, + BTN_RIGHT, +} button_t; + +bool_t hw_init(void); +void hw_release(void); + +void hw_set_lcd_pin(u8_t seg, u8_t com, u8_t val); +void hw_set_button(button_t btn, btn_state_t state); + +void hw_set_buzzer_freq(u4_t freq); +void hw_enable_buzzer(bool_t en); + +#endif /* _HW_H_ */ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/tamalib.c b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/tamalib.c new file mode 100644 index 000000000..92a19678c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/tamalib.c @@ -0,0 +1,128 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "tamalib.h" +#include "hw.h" +#include "cpu.h" +#include "hal.h" + +#define DEFAULT_FRAMERATE 30 // fps + +static exec_mode_t exec_mode = EXEC_MODE_RUN; + +static u32_t step_depth = 0; + +static timestamp_t screen_ts = 0; + +static u32_t ts_freq; + +static u8_t g_framerate = DEFAULT_FRAMERATE; + +hal_t* g_hal; + +bool_t tamalib_init(const u12_t* program, breakpoint_t* breakpoints, u32_t freq) { + bool_t res = 0; + + res |= cpu_init(program, breakpoints, freq); + res |= hw_init(); + + ts_freq = freq; + + return res; +} + +void tamalib_release(void) { + hw_release(); + cpu_release(); +} + +void tamalib_set_framerate(u8_t framerate) { + g_framerate = framerate; +} + +u8_t tamalib_get_framerate(void) { + return g_framerate; +} + +void tamalib_register_hal(hal_t* hal) { + g_hal = hal; +} + +void tamalib_set_exec_mode(exec_mode_t mode) { + exec_mode = mode; + step_depth = cpu_get_depth(); + cpu_sync_ref_timestamp(); +} + +void tamalib_step(void) { + if(exec_mode == EXEC_MODE_PAUSE) { + return; + } + + if(cpu_step()) { + exec_mode = EXEC_MODE_PAUSE; + step_depth = cpu_get_depth(); + } else { + switch(exec_mode) { + case EXEC_MODE_PAUSE: + case EXEC_MODE_RUN: + break; + + case EXEC_MODE_STEP: + exec_mode = EXEC_MODE_PAUSE; + break; + + case EXEC_MODE_NEXT: + if(cpu_get_depth() <= step_depth) { + exec_mode = EXEC_MODE_PAUSE; + step_depth = cpu_get_depth(); + } + break; + + case EXEC_MODE_TO_CALL: + if(cpu_get_depth() > step_depth) { + exec_mode = EXEC_MODE_PAUSE; + step_depth = cpu_get_depth(); + } + break; + + case EXEC_MODE_TO_RET: + if(cpu_get_depth() < step_depth) { + exec_mode = EXEC_MODE_PAUSE; + step_depth = cpu_get_depth(); + } + break; + } + } +} + +void tamalib_mainloop(void) { + timestamp_t ts; + + while(!g_hal->handler()) { + tamalib_step(); + + /* Update the screen @ g_framerate fps */ + ts = g_hal->get_timestamp(); + if(ts - screen_ts >= ts_freq / g_framerate) { + screen_ts = ts; + g_hal->update_screen(); + } + } +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/tamalib.h b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/tamalib.h new file mode 100644 index 000000000..570f845eb --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tama_p1/tamalib/tamalib.h @@ -0,0 +1,65 @@ +/* + * TamaLIB - A hardware agnostic Tamagotchi P1 emulation library + * + * Copyright (C) 2021 Jean-Christophe Rona + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _TAMALIB_H_ +#define _TAMALIB_H_ + +#include "cpu.h" +#include "hw.h" +#include "hal.h" + +#define tamalib_set_button(btn, state) hw_set_button(btn, state) + +#define tamalib_set_speed(speed) cpu_set_speed(speed) + +#define tamalib_get_state() cpu_get_state() +#define tamalib_refresh_hw() cpu_refresh_hw() + +#define tamalib_reset() cpu_reset() + +#define tamalib_add_bp(list, addr) cpu_add_bp(list, addr) +#define tamalib_free_bp(list) cpu_free_bp(list) + +typedef enum { + EXEC_MODE_PAUSE, + EXEC_MODE_RUN, + EXEC_MODE_STEP, + EXEC_MODE_NEXT, + EXEC_MODE_TO_CALL, + EXEC_MODE_TO_RET, +} exec_mode_t; + +void tamalib_release(void); +bool_t tamalib_init(const u12_t* program, breakpoint_t* breakpoints, u32_t freq); + +void tamalib_set_framerate(u8_t framerate); +u8_t tamalib_get_framerate(void); + +void tamalib_register_hal(hal_t* hal); + +void tamalib_set_exec_mode(exec_mode_t mode); + +/* NOTE: Only one of these two functions must be used in the main application + * (tamalib_step() should be used only if tamalib_mainloop() does not fit the + * main application execution flow). + */ +void tamalib_step(void); +void tamalib_mainloop(void); + +#endif /* _TAMALIB_H_ */ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/application.fam b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/application.fam new file mode 100644 index 000000000..d25d634d4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/application.fam @@ -0,0 +1,13 @@ +App( + appid="Tanks", + name="Tanks", + apptype=FlipperAppType.EXTERNAL, + entry_point="tanks_game_app", + cdefines=["APP_TANKS_GAME"], + requires=["gui", "subghz"], + stack_size=4 * 1024, + order=730, + fap_icon="tanksIcon.png", + fap_category="Games_Extra", + fap_icon_assets="images", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/constants.h b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/constants.h new file mode 100644 index 000000000..d65dd32ea --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/constants.h @@ -0,0 +1,19 @@ +#ifndef FLIPPERZERO_FIRMWARE_CONSTANTS_H +#define FLIPPERZERO_FIRMWARE_CONSTANTS_H + +const uint8_t SCREEN_WIDTH_TANKS = 128; +const uint8_t SCREEN_HEIGHT_TANKS = 64; + +const uint8_t FIELD_WIDTH = 16; +const uint8_t FIELD_HEIGHT = 11; + +const uint16_t TURN_LENGTH = 300; +const uint16_t LONG_PRESS_LENGTH = 10; + +const uint8_t SHOT_COOLDOWN = 5; +const uint8_t RESPAWN_COOLDOWN = 8; +const uint8_t PLAYER_RESPAWN_COOLDOWN = 1; + +const uint8_t CELL_LENGTH_PIXELS = 6; + +#endif diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/HappyFlipper_128x64.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/HappyFlipper_128x64.png new file mode 100644 index 000000000..d95412f3f Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/HappyFlipper_128x64.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/TanksSplashScreen_128x64.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/TanksSplashScreen_128x64.png new file mode 100644 index 000000000..a0dc26487 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/TanksSplashScreen_128x64.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/enemy_down.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/enemy_down.png new file mode 100644 index 000000000..84d64ed44 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/enemy_down.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/enemy_left.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/enemy_left.png new file mode 100644 index 000000000..e118d03a8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/enemy_left.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/enemy_right.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/enemy_right.png new file mode 100644 index 000000000..8c5f0603b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/enemy_right.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/enemy_up.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/enemy_up.png new file mode 100644 index 000000000..8ad01f635 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/enemy_up.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/projectile_down.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/projectile_down.png new file mode 100644 index 000000000..fd3f8c123 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/projectile_down.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/projectile_left.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/projectile_left.png new file mode 100644 index 000000000..c8c54786d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/projectile_left.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/projectile_right.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/projectile_right.png new file mode 100644 index 000000000..fbf5359ad Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/projectile_right.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/projectile_up.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/projectile_up.png new file mode 100644 index 000000000..532446657 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/projectile_up.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_base.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_base.png new file mode 100644 index 000000000..11a524ffa Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_base.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_down.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_down.png new file mode 100644 index 000000000..d4e2c8786 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_down.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_explosion.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_explosion.png new file mode 100644 index 000000000..8aab727cd Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_explosion.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_hedgehog.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_hedgehog.png new file mode 100644 index 000000000..fdd804be9 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_hedgehog.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_left.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_left.png new file mode 100644 index 000000000..2c7fd40e0 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_left.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_right.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_right.png new file mode 100644 index 000000000..436d90a61 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_right.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_stone.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_stone.png new file mode 100644 index 000000000..87f89c8da Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_stone.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_up.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_up.png new file mode 100644 index 000000000..3d4e47579 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_up.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_wall.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_wall.png new file mode 100644 index 000000000..88d537cdb Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/images/tank_wall.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/tanksIcon.png b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/tanksIcon.png new file mode 100644 index 000000000..7a2113c0b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/tanksIcon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/tanksgame/tanks_game.c b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/tanks_game.c new file mode 100644 index 000000000..3d67dfbb0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/tanksgame/tanks_game.c @@ -0,0 +1,1456 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "constants.h" + +typedef struct { + // +-----x + // | + // | + // y + uint8_t x; + uint8_t y; +} Point; + +typedef enum { + CellEmpty = 1, + CellWall, + CellExplosion, + CellTankUp, + CellTankRight, + CellTankDown, + CellTankLeft, + CellEnemyUp, + CellEnemyRight, + CellEnemyDown, + CellEnemyLeft, + CellProjectileUp, + CellProjectileRight, + CellProjectileDown, + CellProjectileLeft, +} GameCellState; + +typedef enum { + MenuStateSingleMode, + MenuStateCooperativeServerMode, + MenuStateCooperativeClientMode, +} MenuState; + +typedef enum { + GameStateMenu, + GameStateSingle, + GameStateCooperativeServer, + GameStateCooperativeClient, + GameStateGameOver, +} GameState; + +typedef enum { + DirectionUp, + DirectionRight, + DirectionDown, + DirectionLeft, +} Direction; + +typedef enum { + ModeSingle, + ModeCooperative, +} Mode; + +typedef struct { + Point coordinates; + Direction direction; + bool explosion; + bool is_p1; + bool is_p2; +} ProjectileState; + +typedef struct { + Point coordinates; + uint16_t score; + uint8_t lives; + Direction direction; + bool moving; + bool shooting; + bool live; + uint8_t cooldown; + uint8_t respawn_cooldown; +} PlayerState; + +typedef struct { + // char map[FIELD_WIDTH][FIELD_HEIGHT]; + char thisMap[16][11]; + Point team_one_respawn_points[3]; + Point team_two_respawn_points[3]; + Mode mode; + bool server; + GameState state; + MenuState menu_state; + ProjectileState* projectiles[100]; + PlayerState* bots[6]; + uint8_t enemies_left; + uint8_t enemies_live; + uint8_t enemies_respawn_cooldown; + uint8_t received; + uint8_t sent; + PlayerState* p1; + PlayerState* p2; +} TanksState; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} TanksEvent; + +typedef enum { + GoesUp, + GoesRight, + GoesDown, + GoesLeft, + Shoots, +} ClientAction; + +//char map[FIELD_HEIGHT][FIELD_WIDTH + 1] = { +char thisMap[11][16 + 1] = { + "* - * -", + " - - = ", + " - - 2", + "1 = - -- ", + "-- = - -- ", + "a-1 = - = 2", + "-- = - -- ", + "1 = - -- ", + " - - 2", + " - - = ", + "* - * -", +}; + +static void tanks_game_write_cell(unsigned char* data, int8_t x, int8_t y, GameCellState cell) { + uint8_t index = y * 16 + x; + data[index] = cell; + // if (x % 2) { + // data[index] = (data[index] & 0b00001111) + (cell << 4); + // } else { + // data[index] = (data[index] & 0b0000) + cell; + // } +} + +// Enum with < 16 items => 4 bits in cell, 2 cells in byte +unsigned char* tanks_game_serialize(const TanksState* const tanks_state) { + static unsigned char result[11 * 16 + 1]; + + for(int8_t x = 0; x < FIELD_WIDTH; x++) { + for(int8_t y = 0; y < FIELD_HEIGHT; y++) { + result[(y * FIELD_WIDTH + x)] = 0; + + GameCellState cell = CellEmpty; + + if(tanks_state->thisMap[x][y] == '-') { + cell = CellWall; + + tanks_game_write_cell(result, x, y, cell); + } + } + } + + for(uint8_t i = 0; i < 6; i++) { + if(tanks_state->bots[i] != NULL) { + GameCellState cell = CellEmpty; + + switch(tanks_state->bots[i]->direction) { + case DirectionUp: + cell = CellEnemyUp; + break; + case DirectionDown: + cell = CellEnemyDown; + break; + case DirectionRight: + cell = CellEnemyRight; + break; + case DirectionLeft: + cell = CellEnemyLeft; + break; + default: + break; + } + + tanks_game_write_cell( + result, + tanks_state->bots[i]->coordinates.x, + tanks_state->bots[i]->coordinates.y, + cell); + } + } + + for(int8_t x = 0; x < 100; x++) { + if(tanks_state->projectiles[x] != NULL) { + GameCellState cell = CellEmpty; + + switch(tanks_state->projectiles[x]->direction) { + case DirectionUp: + cell = CellProjectileUp; + break; + case DirectionDown: + cell = CellProjectileDown; + break; + case DirectionRight: + cell = CellProjectileRight; + break; + case DirectionLeft: + cell = CellProjectileLeft; + break; + default: + break; + } + + tanks_game_write_cell( + result, + tanks_state->projectiles[x]->coordinates.x, + tanks_state->projectiles[x]->coordinates.y, + cell); + } + } + + if(tanks_state->p1 != NULL && tanks_state->p1->live) { + GameCellState cell = CellEmpty; + + switch(tanks_state->p1->direction) { + case DirectionUp: + cell = CellTankUp; + break; + case DirectionDown: + cell = CellTankDown; + break; + case DirectionRight: + cell = CellTankRight; + break; + case DirectionLeft: + cell = CellTankLeft; + break; + default: + break; + } + + tanks_game_write_cell( + result, tanks_state->p1->coordinates.x, tanks_state->p1->coordinates.y, cell); + } + + if(tanks_state->p2 != NULL && tanks_state->p2->live) { + GameCellState cell = CellEmpty; + + switch(tanks_state->p2->direction) { + case DirectionUp: + cell = CellTankUp; + break; + case DirectionDown: + cell = CellTankDown; + break; + case DirectionRight: + cell = CellTankRight; + break; + case DirectionLeft: + cell = CellTankLeft; + break; + default: + break; + } + + tanks_game_write_cell( + result, tanks_state->p2->coordinates.x, tanks_state->p2->coordinates.y, cell); + } + + return result; +} + +static void + tanks_game_render_cell(GameCellState cell, uint8_t x, uint8_t y, Canvas* const canvas) { + const Icon* icon; + + if(cell == CellEmpty) { + return; + } + + switch(cell) { + case CellWall: + icon = &I_tank_wall; + break; + case CellExplosion: + icon = &I_tank_explosion; + break; + case CellTankUp: + icon = &I_tank_up; + break; + case CellTankRight: + icon = &I_tank_right; + break; + case CellTankDown: + icon = &I_tank_down; + break; + case CellTankLeft: + icon = &I_tank_left; + break; + case CellEnemyUp: + icon = &I_enemy_up; + break; + case CellEnemyRight: + icon = &I_enemy_right; + break; + case CellEnemyDown: + icon = &I_enemy_down; + break; + case CellEnemyLeft: + icon = &I_enemy_left; + break; + case CellProjectileUp: + icon = &I_projectile_up; + break; + case CellProjectileRight: + icon = &I_projectile_right; + break; + case CellProjectileDown: + icon = &I_projectile_down; + break; + case CellProjectileLeft: + icon = &I_projectile_left; + break; + default: + return; + break; + } + + canvas_draw_icon(canvas, x * CELL_LENGTH_PIXELS, y * CELL_LENGTH_PIXELS - 1, icon); +} + +static void tanks_game_render_constant_cells(Canvas* const canvas) { + for(int8_t x = 0; x < FIELD_WIDTH; x++) { + for(int8_t y = 0; y < FIELD_HEIGHT; y++) { + char cell = thisMap[y][x]; + + if(cell == '=') { + canvas_draw_icon( + canvas, x * CELL_LENGTH_PIXELS, y * CELL_LENGTH_PIXELS - 1, &I_tank_stone); + continue; + } + + if(cell == '*') { + canvas_draw_icon( + canvas, x * CELL_LENGTH_PIXELS, y * CELL_LENGTH_PIXELS - 1, &I_tank_hedgehog); + continue; + } + + if(cell == 'a') { + canvas_draw_icon( + canvas, x * CELL_LENGTH_PIXELS, y * CELL_LENGTH_PIXELS - 1, &I_tank_base); + continue; + } + } + } +} + +void tanks_game_deserialize_and_write_to_state(unsigned char* data, TanksState* const tanks_state) { + for(uint8_t i = 0; i < 11 * 16; i++) { + uint8_t x = i % 16; + uint8_t y = i / 16; + tanks_state->thisMap[x][y] = data[i]; + } +} + +void tanks_game_deserialize_and_render(unsigned char* data, Canvas* const canvas) { + //for (uint8_t i = 0; i < 11 * 16 / 2; i++) { + for(uint8_t i = 0; i < 11 * 16; i++) { + char cell = data[i]; + uint8_t x = i % 16; // One line (16 cells) = 8 bytes + uint8_t y = i / 16; + + // GameCellState first = cell >> 4; + // GameCellState second = cell & 0b00001111; + + tanks_game_render_cell(cell, x, y, canvas); + // tanks_game_render_cell(second, x + 1, y, canvas); + } + + tanks_game_render_constant_cells(canvas); +} + +static void tanks_game_render_callback(Canvas* const canvas, void* ctx) { + const TanksState* tanks_state = acquire_mutex((ValueMutex*)ctx, 25); + if(tanks_state == NULL) { + return; + } + + // Before the function is called, the state is set with the canvas_reset(canvas) + if(tanks_state->state == GameStateMenu) { + canvas_draw_icon(canvas, 0, 0, &I_TanksSplashScreen_128x64); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 124, 10, AlignRight, AlignBottom, "Single"); + canvas_draw_str_aligned(canvas, 124, 25, AlignRight, AlignBottom, "Co-op S"); + canvas_draw_str_aligned(canvas, 124, 40, AlignRight, AlignBottom, "Co-op C"); + + switch(tanks_state->menu_state) { + case MenuStateSingleMode: + canvas_draw_icon(canvas, 74, 3, &I_tank_right); + break; + case MenuStateCooperativeServerMode: + canvas_draw_icon(canvas, 74, 18, &I_tank_right); + break; + case MenuStateCooperativeClientMode: + canvas_draw_icon(canvas, 74, 33, &I_tank_right); + break; + } + + canvas_draw_frame(canvas, 0, 0, 128, 64); + + release_mutex((ValueMutex*)ctx, tanks_state); + return; + } + + // Field right border + canvas_draw_box(canvas, FIELD_WIDTH * CELL_LENGTH_PIXELS, 0, 2, SCREEN_HEIGHT_TANKS); + + // Cooperative client + if(tanks_state->mode == ModeCooperative && !tanks_state->server) { + for(int8_t x = 0; x < FIELD_WIDTH; x++) { + for(int8_t y = 0; y < FIELD_HEIGHT; y++) { + tanks_game_render_cell(tanks_state->thisMap[x][y], x, y, canvas); + } + } + + tanks_game_render_constant_cells(canvas); + + release_mutex((ValueMutex*)ctx, tanks_state); + return; + } + + // Player + // Point coordinates = tanks_state->p1->coordinates; + // const Icon *icon; + // switch (tanks_state->p1->direction) { + // case DirectionUp: + // icon = &I_tank_up; + // break; + // case DirectionDown: + // icon = &I_tank_down; + // break; + // case DirectionRight: + // icon = &I_tank_right; + // break; + // case DirectionLeft: + // icon = &I_tank_left; + // break; + // default: + // icon = &I_tank_explosion; + // } + + // if (tanks_state->p1->live) { + // canvas_draw_icon(canvas, coordinates.x * CELL_LENGTH_PIXELS, coordinates.y * CELL_LENGTH_PIXELS - 1, icon); + // } + // + // for(int8_t x = 0; x < FIELD_WIDTH; x++) { + // for(int8_t y = 0; y < FIELD_HEIGHT; y++) { + // switch (tanks_state->thisMap[x][y]) { + // case '-': + // canvas_draw_icon(canvas, x * CELL_LENGTH_PIXELS, y * CELL_LENGTH_PIXELS - 1, &I_tank_wall); + // break; + // + // case '=': + // canvas_draw_icon(canvas, x * CELL_LENGTH_PIXELS, y * CELL_LENGTH_PIXELS - 1, &I_tank_stone); + // break; + // + // case '*': + // canvas_draw_icon(canvas, x * CELL_LENGTH_PIXELS, y * CELL_LENGTH_PIXELS - 1, &I_tank_hedgehog); + // break; + // + // case 'a': + // canvas_draw_icon(canvas, x * CELL_LENGTH_PIXELS, y * CELL_LENGTH_PIXELS - 1, &I_tank_base); + // break; + // } + // } + // } + + // for ( + // uint8_t i = 0; + // i < 6; + // i++ + // ) { + // if (tanks_state->bots[i] != NULL) { + // const Icon *icon; + // + // switch(tanks_state->bots[i]->direction) { + // case DirectionUp: + // icon = &I_enemy_up; + // break; + // case DirectionDown: + // icon = &I_enemy_down; + // break; + // case DirectionRight: + // icon = &I_enemy_right; + // break; + // case DirectionLeft: + // icon = &I_enemy_left; + // break; + // default: + // icon = &I_tank_explosion; + // } + // + // canvas_draw_icon( + // canvas, + // tanks_state->bots[i]->coordinates.x * CELL_LENGTH_PIXELS, + // tanks_state->bots[i]->coordinates.y * CELL_LENGTH_PIXELS - 1, + // icon); + // } + // } + + // for(int8_t x = 0; x < 100; x++) { + // if (tanks_state->projectiles[x] != NULL) { + // ProjectileState *projectile = tanks_state->projectiles[x]; + // + // if (projectile->explosion) { + // canvas_draw_icon( + // canvas, + // projectile->coordinates.x * CELL_LENGTH_PIXELS, + // projectile->coordinates.y * CELL_LENGTH_PIXELS - 1, + // &I_tank_explosion); + // continue; + // } + // + // const Icon *icon; + // + // switch(projectile->direction) { + // case DirectionUp: + // icon = &I_projectile_up; + // break; + // case DirectionDown: + // icon = &I_projectile_down; + // break; + // case DirectionRight: + // icon = &I_projectile_right; + // break; + // case DirectionLeft: + // icon = &I_projectile_left; + // break; + // default: + // icon = &I_tank_explosion; + // } + // + // canvas_draw_icon( + // canvas, + // projectile->coordinates.x * CELL_LENGTH_PIXELS, + // projectile->coordinates.y * CELL_LENGTH_PIXELS - 1, + // icon); + // } + // } + + // Info + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + char buffer1[13]; + snprintf(buffer1, sizeof(buffer1), "live: %u", tanks_state->enemies_live); + canvas_draw_str_aligned(canvas, 127, 8, AlignRight, AlignBottom, buffer1); + + snprintf(buffer1, sizeof(buffer1), "left: %u", tanks_state->enemies_left); + canvas_draw_str_aligned(canvas, 127, 18, AlignRight, AlignBottom, buffer1); + + snprintf(buffer1, sizeof(buffer1), "p1 l: %u", tanks_state->p1->lives); + canvas_draw_str_aligned(canvas, 127, 28, AlignRight, AlignBottom, buffer1); + + snprintf(buffer1, sizeof(buffer1), "p1 s: %u", tanks_state->p1->score); + canvas_draw_str_aligned(canvas, 127, 38, AlignRight, AlignBottom, buffer1); + + if(tanks_state->state == GameStateCooperativeServer && tanks_state->p2) { + snprintf(buffer1, sizeof(buffer1), "rec: %u", tanks_state->received); + canvas_draw_str_aligned(canvas, 127, 48, AlignRight, AlignBottom, buffer1); + + snprintf(buffer1, sizeof(buffer1), "snt: %u", tanks_state->sent); + canvas_draw_str_aligned(canvas, 127, 58, AlignRight, AlignBottom, buffer1); + // snprintf(buffer1, sizeof(buffer1), "p2 l: %u", tanks_state->p2->lives); + // canvas_draw_str_aligned(canvas, 127, 48, AlignRight, AlignBottom, buffer1); + // + // snprintf(buffer1, sizeof(buffer1), "p2 s: %u", tanks_state->p2->score); + // canvas_draw_str_aligned(canvas, 127, 58, AlignRight, AlignBottom, buffer1); + } + + if(tanks_state->state == GameStateCooperativeClient) { + snprintf(buffer1, sizeof(buffer1), "rec: %u", tanks_state->received); + canvas_draw_str_aligned(canvas, 127, 48, AlignRight, AlignBottom, buffer1); + } + + // Game Over banner + if(tanks_state->state == GameStateGameOver) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 34, 20, 62, 24); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, 34, 20, 62, 24); + canvas_set_font(canvas, FontPrimary); + + if(tanks_state->enemies_left == 0 && tanks_state->enemies_live == 0) { + canvas_draw_str(canvas, 37, 31, "You win!"); + } else { + canvas_draw_str(canvas, 37, 31, "Game Over"); + } + + canvas_set_font(canvas, FontSecondary); + char buffer[13]; + snprintf(buffer, sizeof(buffer), "Score: %u", tanks_state->p1->score); + canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer); + } + + // TEST start + unsigned char* data = tanks_game_serialize(tanks_state); + tanks_game_deserialize_and_render(data, canvas); + // TEST enf + + release_mutex((ValueMutex*)ctx, tanks_state); +} + +static void tanks_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + TanksEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void tanks_game_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + TanksEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static bool tanks_get_cell_is_free(TanksState* const tanks_state, Point point) { + // Tiles + if(tanks_state->thisMap[point.x][point.y] != ' ') { + return false; + } + + // Projectiles + for(int8_t x = 0; x < 100; x++) { + if(tanks_state->projectiles[x] != NULL) { + if(tanks_state->projectiles[x]->coordinates.x == point.x && + tanks_state->projectiles[x]->coordinates.y == point.y) { + return false; + } + } + } + + // Player 1 + if(tanks_state->p1 != NULL) { + if(tanks_state->p1->coordinates.x == point.x && + tanks_state->p1->coordinates.y == point.y) { + return false; + } + } + + // Player 2 + if(tanks_state->p2 != NULL) { + if(tanks_state->p2->coordinates.x == point.x && + tanks_state->p2->coordinates.y == point.y) { + return false; + } + } + + // Bots + for(int8_t x = 0; x < 6; x++) { + if(tanks_state->bots[x] != NULL) { + if(tanks_state->bots[x]->coordinates.x == point.x && + tanks_state->bots[x]->coordinates.y == point.y) { + return false; + } + } + } + + return true; +} + +static uint8_t tanks_get_random_free_respawn_point_index( + TanksState* const tanks_state, + Point respawn_points[3]) { + uint8_t first = rand() % 3; + int8_t add = rand() % 2 ? +1 : -1; + int8_t second = first + add; + uint8_t third; + + if(second == 4) { + second = 0; + } else if(second == -1) { + second = 3; + } + + for(uint8_t i = 0; i < 3; i++) { + if(i != first && i != second) { + third = i; + } + } + + if(tanks_get_cell_is_free(tanks_state, respawn_points[first])) { + return first; + } + + if(tanks_get_cell_is_free(tanks_state, respawn_points[second])) { + return second; + } + + if(tanks_get_cell_is_free(tanks_state, respawn_points[third])) { + return third; + } + + return -1; +} + +static void tanks_game_init_game(TanksState* const tanks_state, GameState type) { + srand(DWT->CYCCNT); + + tanks_state->state = type; + + for(int8_t x = 0; x < 100; x++) { + if(tanks_state->projectiles[x] != NULL) { + free(tanks_state->projectiles[x]); + tanks_state->projectiles[x] = NULL; + } + } + + int8_t team_one_respawn_points_counter = 0; + int8_t team_two_respawn_points_counter = 0; + + for(int8_t x = 0; x < FIELD_WIDTH; x++) { + for(int8_t y = 0; y < FIELD_HEIGHT; y++) { + tanks_state->thisMap[x][y] = ' '; + + if(thisMap[y][x] == '1') { + Point respawn = {x, y}; + tanks_state->team_one_respawn_points[team_one_respawn_points_counter++] = respawn; + } + + if(thisMap[y][x] == '2') { + Point respawn = {x, y}; + tanks_state->team_two_respawn_points[team_two_respawn_points_counter++] = respawn; + } + + if(thisMap[y][x] == '-') { + tanks_state->thisMap[x][y] = '-'; + } + + if(thisMap[y][x] == '=') { + tanks_state->thisMap[x][y] = '='; + } + + if(thisMap[y][x] == '*') { + tanks_state->thisMap[x][y] = '*'; + } + + if(thisMap[y][x] == 'a') { + tanks_state->thisMap[x][y] = 'a'; + } + } + } + + uint8_t index1 = tanks_get_random_free_respawn_point_index( + tanks_state, tanks_state->team_one_respawn_points); + Point c = { + tanks_state->team_one_respawn_points[index1].x, + tanks_state->team_one_respawn_points[index1].y}; + + PlayerState p1 = { + c, + 0, + 4, + DirectionRight, + 0, + 0, + 1, + SHOT_COOLDOWN, + PLAYER_RESPAWN_COOLDOWN, + }; + + PlayerState* p1_state = malloc(sizeof(PlayerState)); + *p1_state = p1; + + tanks_state->p1 = p1_state; + + if(type == GameStateCooperativeServer) { + int8_t index2 = tanks_get_random_free_respawn_point_index( + tanks_state, tanks_state->team_one_respawn_points); + Point c = { + tanks_state->team_one_respawn_points[index2].x, + tanks_state->team_one_respawn_points[index2].y}; + + PlayerState p2 = { + c, + 0, + 4, + DirectionRight, + 0, + 0, + 1, + SHOT_COOLDOWN, + PLAYER_RESPAWN_COOLDOWN, + }; + + PlayerState* p2_state = malloc(sizeof(PlayerState)); + *p2_state = p2; + + tanks_state->p2 = p2_state; + } + + tanks_state->enemies_left = 5; + tanks_state->enemies_live = 0; + tanks_state->enemies_respawn_cooldown = RESPAWN_COOLDOWN; + tanks_state->received = 0; + tanks_state->sent = 0; + + if(type == GameStateCooperativeClient) { + for(int8_t x = 0; x < FIELD_WIDTH; x++) { + for(int8_t y = 0; y < FIELD_HEIGHT; y++) { + tanks_state->thisMap[x][y] = CellEmpty; + } + } + } +} + +static bool + tanks_game_collision(Point const next_step, bool shoot, TanksState const* const tanks_state) { + if((int8_t)next_step.x < 0 || (int8_t)next_step.y < 0) { + return true; + } + + if(next_step.x >= FIELD_WIDTH || next_step.y >= FIELD_HEIGHT) { + return true; + } + + char tile = tanks_state->thisMap[next_step.x][next_step.y]; + + if(tile == '*' && !shoot) { + return true; + } + + if(tile == '-' || tile == '=' || tile == 'a') { + return true; + } + + for(uint8_t i = 0; i < 6; i++) { + if(tanks_state->bots[i] != NULL) { + if(tanks_state->bots[i]->coordinates.x == next_step.x && + tanks_state->bots[i]->coordinates.y == next_step.y) { + return true; + } + } + } + + if(tanks_state->p1 != NULL && tanks_state->p1->live && + tanks_state->p1->coordinates.x == next_step.x && + tanks_state->p1->coordinates.y == next_step.y) { + return true; + } + + if(tanks_state->p2 != NULL && tanks_state->p2->live && + tanks_state->p2->coordinates.x == next_step.x && + tanks_state->p2->coordinates.y == next_step.y) { + return true; + } + + return false; +} + +static Point tanks_game_get_next_step(Point coordinates, Direction direction) { + Point next_step = {coordinates.x, coordinates.y}; + + switch(direction) { + // +-----x + // | + // | + // y + case DirectionUp: + next_step.y--; + break; + case DirectionRight: + next_step.x++; + break; + case DirectionDown: + next_step.y++; + break; + case DirectionLeft: + next_step.x--; + break; + default: + break; + } + return next_step; +} + +static uint8_t tanks_game_get_free_projectile_index(TanksState* const tanks_state) { + uint8_t freeProjectileIndex; + for(freeProjectileIndex = 0; freeProjectileIndex < 100; freeProjectileIndex++) { + if(tanks_state->projectiles[freeProjectileIndex] == NULL) { + return freeProjectileIndex; + } + } + + return 0; +} + +static void tanks_game_shoot( + TanksState* const tanks_state, + PlayerState* tank_state, + bool is_p1, + bool is_p2) { + tank_state->cooldown = SHOT_COOLDOWN; + + uint8_t freeProjectileIndex = tanks_game_get_free_projectile_index(tanks_state); + + ProjectileState* projectile_state = malloc(sizeof(ProjectileState)); + Point next_step = tanks_game_get_next_step(tank_state->coordinates, tank_state->direction); + + projectile_state->direction = tank_state->direction; + projectile_state->coordinates = next_step; + projectile_state->is_p1 = is_p1; + projectile_state->is_p2 = is_p2; + + bool crush = tanks_game_collision(projectile_state->coordinates, true, tanks_state); + projectile_state->explosion = crush; + + tanks_state->projectiles[freeProjectileIndex] = projectile_state; +} + +static void tanks_game_process_game_step(TanksState* const tanks_state) { + if(tanks_state->state == GameStateMenu) { + return; + } + + if(tanks_state->enemies_left == 0 && tanks_state->enemies_live == 0) { + tanks_state->state = GameStateGameOver; + } + + if(!tanks_state->p1->live && tanks_state->p1->lives == 0) { + tanks_state->state = GameStateGameOver; + } + + if(tanks_state->state == GameStateGameOver) { + return; + } + + if(tanks_state->p1 != NULL) { + if(!tanks_state->p1->live && tanks_state->p1->respawn_cooldown > 0) { + tanks_state->p1->respawn_cooldown--; + } + } + + // Player 1 spawn + if(tanks_state->p1 && !tanks_state->p1->live && tanks_state->p1->lives > 0) { + int8_t index = tanks_get_random_free_respawn_point_index( + tanks_state, tanks_state->team_one_respawn_points); + + if(index != -1) { + Point point = tanks_state->team_one_respawn_points[index]; + Point c = {point.x, point.y}; + tanks_state->p1->coordinates = c; + tanks_state->p1->live = true; + tanks_state->p1->direction = DirectionRight; + tanks_state->p1->cooldown = SHOT_COOLDOWN; + tanks_state->p1->respawn_cooldown = SHOT_COOLDOWN; + } + } + + // Player 2 spawn + if(tanks_state->state == GameStateCooperativeServer && tanks_state->p2 && + !tanks_state->p2->live && tanks_state->p2->lives > 0) { + int8_t index = tanks_get_random_free_respawn_point_index( + tanks_state, tanks_state->team_one_respawn_points); + + if(index != -1) { + Point point = tanks_state->team_one_respawn_points[index]; + Point c = {point.x, point.y}; + tanks_state->p2->coordinates = c; + tanks_state->p2->live = true; + tanks_state->p2->direction = DirectionRight; + tanks_state->p2->cooldown = SHOT_COOLDOWN; + tanks_state->p2->respawn_cooldown = SHOT_COOLDOWN; + } + } + + // Bot turn + for(uint8_t i = 0; i < 6; i++) { + if(tanks_state->bots[i] != NULL) { + PlayerState* bot = tanks_state->bots[i]; + if(bot->cooldown) { + bot->cooldown--; + } + + // Rotate + if(rand() % 3 == 0) { + bot->direction = (rand() % 4); + } + + // Move + if(rand() % 2 == 0) { + Point next_step = tanks_game_get_next_step(bot->coordinates, bot->direction); + bool crush = tanks_game_collision(next_step, false, tanks_state); + + if(!crush) { + bot->coordinates = next_step; + } + } + + // Shoot + if(bot->cooldown == 0 && rand() % 3 != 0) { + tanks_game_shoot(tanks_state, bot, false, false); + } + } + } + + // Bot spawn + if(tanks_state->enemies_respawn_cooldown) { + tanks_state->enemies_respawn_cooldown--; + } + + if(tanks_state->enemies_left > 0 && tanks_state->enemies_live <= 4 && + tanks_state->enemies_respawn_cooldown == 0) { + int8_t index = tanks_get_random_free_respawn_point_index( + tanks_state, tanks_state->team_two_respawn_points); + + if(index != -1) { + tanks_state->enemies_left--; + tanks_state->enemies_live++; + tanks_state->enemies_respawn_cooldown = RESPAWN_COOLDOWN; + Point point = tanks_state->team_two_respawn_points[index]; + + Point c = {point.x, point.y}; + + PlayerState bot = { + c, + 0, + 0, + DirectionLeft, + 0, + 0, + 1, + SHOT_COOLDOWN, + PLAYER_RESPAWN_COOLDOWN, + }; + + uint8_t freeEnemyIndex; + for(freeEnemyIndex = 0; freeEnemyIndex < 6; freeEnemyIndex++) { + if(tanks_state->bots[freeEnemyIndex] == NULL) { + break; + } + } + + PlayerState* bot_state = malloc(sizeof(PlayerState)); + *bot_state = bot; + + tanks_state->bots[freeEnemyIndex] = bot_state; + } + } + + if(tanks_state->p1 != NULL && tanks_state->p1->live && tanks_state->p1->moving) { + Point next_step = + tanks_game_get_next_step(tanks_state->p1->coordinates, tanks_state->p1->direction); + bool crush = tanks_game_collision(next_step, false, tanks_state); + + if(!crush) { + tanks_state->p1->coordinates = next_step; + } + } + + // Player 2 spawn + if(tanks_state->state == GameStateCooperativeServer && tanks_state->p2 && + tanks_state->p2->live && tanks_state->p2->moving) { + Point next_step = + tanks_game_get_next_step(tanks_state->p2->coordinates, tanks_state->p2->direction); + bool crush = tanks_game_collision(next_step, false, tanks_state); + + if(!crush) { + tanks_state->p2->coordinates = next_step; + } + } + + for(int8_t x = 0; x < 100; x++) { + if(tanks_state->projectiles[x] != NULL) { + ProjectileState* projectile = tanks_state->projectiles[x]; + Point c = projectile->coordinates; + + if(projectile->explosion) { + // Break a wall + if(tanks_state->thisMap[c.x][c.y] == '-') { + tanks_state->thisMap[c.x][c.y] = ' '; + } + + // Kill a bot + for(uint8_t i = 0; i < 6; i++) { + if(tanks_state->bots[i] != NULL) { + if(tanks_state->bots[i]->coordinates.x == c.x && + tanks_state->bots[i]->coordinates.y == c.y) { + if(projectile->is_p1) { + tanks_state->p1->score++; + } + + if(projectile->is_p2) { + tanks_state->p2->score++; + } + + // No friendly fire + if(projectile->is_p1 || projectile->is_p2) { + tanks_state->enemies_live--; + free(tanks_state->bots[i]); + tanks_state->bots[i] = NULL; + } + } + } + } + + // Destroy the flag + if(tanks_state->thisMap[c.x][c.y] == 'a') { + tanks_state->state = GameStateGameOver; + return; + } + + // Kill a player 1 + if(tanks_state->p1 != NULL) { + if(tanks_state->p1->live && tanks_state->p1->coordinates.x == c.x && + tanks_state->p1->coordinates.y == c.y) { + tanks_state->p1->live = false; + tanks_state->p1->lives--; + tanks_state->p1->respawn_cooldown = PLAYER_RESPAWN_COOLDOWN; + } + } + + // Kill a player 2 + if(tanks_state->p2 != NULL) { + if(tanks_state->p2->live && tanks_state->p2->coordinates.x == c.x && + tanks_state->p2->coordinates.y == c.y) { + tanks_state->p2->live = false; + tanks_state->p2->lives--; + tanks_state->p2->respawn_cooldown = PLAYER_RESPAWN_COOLDOWN; + } + } + + // Delete projectile + free(tanks_state->projectiles[x]); + tanks_state->projectiles[x] = NULL; + continue; + } + + Point next_step = + tanks_game_get_next_step(projectile->coordinates, projectile->direction); + bool crush = tanks_game_collision(next_step, true, tanks_state); + projectile->coordinates = next_step; + + if(crush) { + projectile->explosion = true; + } + } + } + + if(tanks_state->p1->cooldown > 0) { + tanks_state->p1->cooldown--; + } + + if(tanks_state->p2 != NULL && tanks_state->p2->cooldown > 0) { + tanks_state->p2->cooldown--; + } + + if(tanks_state->p1 != NULL && tanks_state->p1->live && tanks_state->p1->shooting && + tanks_state->p1->cooldown == 0) { + tanks_game_shoot(tanks_state, tanks_state->p1, true, false); + } + + tanks_state->p1->moving = false; + tanks_state->p1->shooting = false; + + if(tanks_state->p2 != NULL) { + tanks_state->p2->moving = false; + tanks_state->p2->shooting = false; + } +} + +int32_t tanks_game_app(void* p) { + UNUSED(p); + srand(DWT->CYCCNT); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(TanksEvent)); + + TanksState* tanks_state = malloc(sizeof(TanksState)); + + tanks_state->state = GameStateMenu; + tanks_state->menu_state = MenuStateSingleMode; + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, tanks_state, sizeof(TanksState))) { + FURI_LOG_E("Tanks", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(tanks_state); + return 255; + } + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, tanks_game_render_callback, &state_mutex); + view_port_input_callback_set(view_port, tanks_game_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(tanks_game_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + TanksEvent event; + + // Initialize network thing. + uint32_t frequency = 433920000; + size_t message_max_len = 180; + uint8_t incomingMessage[180] = {0}; + SubGhzTxRxWorker* subghz_txrx = subghz_tx_rx_worker_alloc(); + subghz_tx_rx_worker_start(subghz_txrx, frequency); + furi_hal_power_suppress_charge_enter(); + + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + + TanksState* tanks_state = (TanksState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + if(tanks_state->state == GameStateMenu) { + if(tanks_state->menu_state == MenuStateCooperativeServerMode) { + tanks_state->menu_state = MenuStateSingleMode; + } else if(tanks_state->menu_state == MenuStateCooperativeClientMode) { + tanks_state->menu_state = MenuStateCooperativeServerMode; + } + } else if(tanks_state->state == GameStateCooperativeClient) { + FuriString* goesUp = NULL; + char arr[2]; + arr[0] = GoesUp; + arr[1] = 0; + furi_string_set(goesUp, (char*)&arr); + + subghz_tx_rx_worker_write( + subghz_txrx, + (uint8_t*)furi_string_get_cstr(goesUp), + strlen(furi_string_get_cstr(goesUp))); + + } else { + tanks_state->p1->moving = true; + tanks_state->p1->direction = DirectionUp; + } + break; + case InputKeyDown: + if(tanks_state->state == GameStateMenu) { + if(tanks_state->menu_state == MenuStateSingleMode) { + tanks_state->menu_state = MenuStateCooperativeServerMode; + } else if(tanks_state->menu_state == MenuStateCooperativeServerMode) { + tanks_state->menu_state = MenuStateCooperativeClientMode; + } + } else if(tanks_state->state == GameStateCooperativeClient) { + FuriString* goesDown = NULL; + char arr[2]; + arr[0] = GoesDown; + arr[1] = 0; + furi_string_set(goesDown, (char*)&arr); + + subghz_tx_rx_worker_write( + subghz_txrx, + (uint8_t*)furi_string_get_cstr(goesDown), + strlen(furi_string_get_cstr(goesDown))); + } else { + tanks_state->p1->moving = true; + tanks_state->p1->direction = DirectionDown; + } + break; + case InputKeyRight: + if(tanks_state->state == GameStateCooperativeClient) { + FuriString* goesRight = NULL; + char arr[2]; + arr[0] = GoesRight; + arr[1] = 0; + furi_string_set(goesRight, (char*)&arr); + + subghz_tx_rx_worker_write( + subghz_txrx, + (uint8_t*)furi_string_get_cstr(goesRight), + strlen(furi_string_get_cstr(goesRight))); + } else { + tanks_state->p1->moving = true; + tanks_state->p1->direction = DirectionRight; + } + break; + case InputKeyLeft: + if(tanks_state->state == GameStateCooperativeClient) { + FuriString* goesLeft = NULL; + char arr[2]; + arr[0] = GoesLeft; + arr[1] = 0; + furi_string_set(goesLeft, (char*)&arr); + + subghz_tx_rx_worker_write( + subghz_txrx, + (uint8_t*)furi_string_get_cstr(goesLeft), + strlen(furi_string_get_cstr(goesLeft))); + } else { + tanks_state->p1->moving = true; + tanks_state->p1->direction = DirectionLeft; + } + break; + case InputKeyOk: + if(tanks_state->state == GameStateMenu) { + if(tanks_state->menu_state == MenuStateSingleMode) { + tanks_state->server = true; + tanks_game_init_game(tanks_state, GameStateSingle); + break; + } else if(tanks_state->menu_state == MenuStateCooperativeServerMode) { + tanks_state->server = true; + tanks_game_init_game(tanks_state, GameStateCooperativeServer); + break; + } else if(tanks_state->menu_state == MenuStateCooperativeClientMode) { + tanks_state->server = false; + tanks_game_init_game(tanks_state, GameStateCooperativeClient); + break; + } + } else if(tanks_state->state == GameStateGameOver) { + tanks_game_init_game(tanks_state, tanks_state->state); + } else if(tanks_state->state == GameStateCooperativeClient) { + FuriString* shoots = NULL; + char arr[2]; + arr[0] = Shoots; + arr[1] = 0; + furi_string_set(shoots, (char*)&arr); + + subghz_tx_rx_worker_write( + subghz_txrx, + (uint8_t*)furi_string_get_cstr(shoots), + strlen(furi_string_get_cstr(shoots))); + } else { + tanks_state->p1->shooting = true; + } + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + if(tanks_state->state == GameStateCooperativeServer) { + if(subghz_tx_rx_worker_available(subghz_txrx)) { + memset(incomingMessage, 0x00, message_max_len); + subghz_tx_rx_worker_read(subghz_txrx, incomingMessage, message_max_len); + + if(incomingMessage != NULL) { + tanks_state->received++; + + switch(incomingMessage[0]) { + case GoesUp: + tanks_state->p2->moving = true; + tanks_state->p2->direction = DirectionUp; + break; + case GoesRight: + tanks_state->p2->moving = true; + tanks_state->p2->direction = DirectionRight; + break; + case GoesDown: + tanks_state->p2->moving = true; + tanks_state->p2->direction = DirectionDown; + break; + case GoesLeft: + tanks_state->p2->moving = true; + tanks_state->p2->direction = DirectionLeft; + break; + case Shoots: + tanks_state->p2->shooting = true; + break; + default: + break; + } + } + } + + tanks_game_process_game_step(tanks_state); + + FuriString* serializedData = NULL; + unsigned char* data = tanks_game_serialize(tanks_state); + char arr[11 * 16 + 1]; + + for(uint8_t i = 0; i < 11 * 16; i++) { + arr[i] = data[i]; + } + + arr[11 * 16] = 0; + + furi_string_set(serializedData, (char*)&arr); + + subghz_tx_rx_worker_write( + subghz_txrx, + (uint8_t*)furi_string_get_cstr(serializedData), + strlen(furi_string_get_cstr(serializedData))); + + tanks_state->sent++; + } else if(tanks_state->state == GameStateSingle) { + tanks_game_process_game_step(tanks_state); + } else if(tanks_state->state == GameStateCooperativeClient) { + if(subghz_tx_rx_worker_available(subghz_txrx)) { + memset(incomingMessage, 0x00, message_max_len); + subghz_tx_rx_worker_read(subghz_txrx, incomingMessage, message_max_len); + + tanks_state->received++; + + tanks_game_deserialize_and_write_to_state( + (unsigned char*)incomingMessage, tanks_state); + } + } + } + } else { + // event timeout + } + + view_port_update(view_port); + release_mutex(&state_mutex, tanks_state); + furi_delay_ms(1); + } + + furi_delay_ms(10); + furi_hal_power_suppress_charge_exit(); + + if(subghz_tx_rx_worker_is_running(subghz_txrx)) { + subghz_tx_rx_worker_stop(subghz_txrx); + subghz_tx_rx_worker_free(subghz_txrx); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + + if(tanks_state->p1 != NULL) { + free(tanks_state->p1); + } + + if(tanks_state->p2 != NULL) { + free(tanks_state->p2); + } + + free(tanks_state); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/.gitattributes b/Applications/Official/DEV_FW/source/xMasterX/timelapse/.gitattributes new file mode 100644 index 000000000..cd63b2f7b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/timelapse/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +#* text=auto diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/.gitignore b/Applications/Official/DEV_FW/source/xMasterX/timelapse/.gitignore new file mode 100644 index 000000000..722d5e71d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/timelapse/.gitignore @@ -0,0 +1 @@ +.vscode diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/README.md b/Applications/Official/DEV_FW/source/xMasterX/timelapse/README.md new file mode 100644 index 000000000..b10955ea1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/timelapse/README.md @@ -0,0 +1,78 @@ + +# zeitraffer + +[![Build FAP](https://github.com/theageoflove/flipperzero-zeitraffer/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/theageoflove/flipperzero-zeitraffer/actions/workflows/build.yml) + +english version [below](#eng) + + +Blog: [theageoflove.ru](https://theageoflove.ru) + +TG: [t.me/scuko_bled](https://t.me/scuko_bled) + + +![zeitraffer for flipper zero](https://theageoflove.ru/uploads/2022/11/photo_2022-11-10_15-54-25.jpg) +Видео работы: https://youtu.be/VPSpRLJXYAc + +Готовый фап под последнюю релизную прошивку [можно скачать здесь](https://nightly.link/theageoflove/flipperzero-zeitraffer/workflows/build/main/zeitraffer.fap.zip). + +Я ненастоящий сварщик, не обессудьте. Делал для своей Sony DSLR A100, подходит для любых камер, поддерживающих проводной пульт с тремя контактами. + +Основано на хелловорлде https://github.com/zmactep/flipperzero-hello-world + +### Управление: + + - **вверх-вниз** - время. + - **влево-вправо** - количество кадров + + 0 кадров - бесконечный режим, -1 кадров - BULB + - **зажатие стрелок** - ±10 кадров/секунд + - **ОК** - пуск/пауза + - Длинное нажатие **ОК** - включить/выключить подсветку + - **назад** - сброс + - длинное нажатие **назад** - выход + +При работающем таймере блокируются все кнопки кроме ОК. + +При запуске даётся три секунды на отскочить. + +## Чо надо + - две оптопары типа EL817C + - кусок гребёнки на три пина + - немного провода + - термоусадка + - разъём пульта от камеры. Где взять или из чего сделать - думайте + +## Как собрать +Берём оптопары, соединяем по схеме. +![](https://theageoflove.ru/uploads/2022/11/camera_cable.jpg) +Где какой пин у камеры, можно узнать например тут: https://www.doc-diy.net/photo/remote_pinout/ + +# English +Simple timelapse app for Flipper Zero. + +[Get latest release](https://nightly.link/theageoflove/flipperzero-zeitraffer/workflows/build/main/zeitraffer.fap.zip) + +based on https://github.com/zmactep/flipperzero-hello-world + +### Control: + - Up and down - time. + - Left and right - number of frames + - Long press arrows - ±10 frames/seconds + - OK - start/pause + - Long press OK - turn on/off the backlight + - Back - reset + - Long press back - exit + +When the timer is running, all buttons are blocked except OK. + +## What you need: + - two EL817C optocouplers + - pin header connector 1x3 2,54mm male + - some wire + - heat shrink + - camera remote connector +## How to assemble +Take optocouplers, connect according to the scheme. +![](https://theageoflove.ru/uploads/2022/11/camera_cable_en.jpg) +Camera pinout can be found here: https://www.doc-diy.net/photo/remote_pinout/ diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/application.fam b/Applications/Official/DEV_FW/source/xMasterX/timelapse/application.fam new file mode 100644 index 000000000..12ed589ff --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/timelapse/application.fam @@ -0,0 +1,21 @@ +App( + appid="GPIO_Timelapse", + name="[GPIO] Timelapse", + apptype=FlipperAppType.EXTERNAL, + entry_point="zeitraffer_app", + cdefines=["APP_ZEITRAFFER"], + requires=[ + "gui", + "input", + "notification", + "gpio" + ], + stack_size=2 * 1024, + order=90, + fap_icon_assets="icons", + fap_icon="zeitraffer.png", + fap_category="GPIO_Extra", + fap_description="Simple intervalometer app", + fap_author="Aurelius Rosenbaum", + fap_weburl="https://github.com/theageoflove/flipperzero-zeitraffer", +) \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/gpio_item.c b/Applications/Official/DEV_FW/source/xMasterX/timelapse/gpio_item.c new file mode 100644 index 000000000..2d0f5f676 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/timelapse/gpio_item.c @@ -0,0 +1,51 @@ +#include "gpio_item.h" + +#include + +typedef struct { + const char* name; + const GpioPin* pin; +} GpioItem; + +static const GpioItem gpio_item[GPIO_ITEM_COUNT] = { + {"1.2: PA7", &gpio_ext_pa7}, + {"1.3: PA6", &gpio_ext_pa6}, + {"1.4: PA4", &gpio_ext_pa4}, + {"1.5: PB3", &gpio_ext_pb3}, + {"1.6: PB2", &gpio_ext_pb2}, + {"1.7: PC3", &gpio_ext_pc3}, + {"2.7: PC1", &gpio_ext_pc1}, + {"2.8: PC0", &gpio_ext_pc0}, +}; + +void gpio_item_configure_pin(uint8_t index, GpioMode mode) { + furi_assert(index < GPIO_ITEM_COUNT); + furi_hal_gpio_write(gpio_item[index].pin, false); + furi_hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); +} + +void gpio_item_configure_all_pins(GpioMode mode) { + for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { + gpio_item_configure_pin(i, mode); + } +} + +void gpio_item_set_pin(uint8_t index, bool level) { + furi_assert(index < GPIO_ITEM_COUNT); + furi_hal_gpio_write(gpio_item[index].pin, level); +} + +void gpio_item_set_all_pins(bool level) { + for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { + gpio_item_set_pin(i, level); + } +} + +const char* gpio_item_get_pin_name(uint8_t index) { + furi_assert(index < GPIO_ITEM_COUNT + 1); + if(index == GPIO_ITEM_COUNT) { + return "ALL"; + } else { + return gpio_item[index].name; + } +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/gpio_item.h b/Applications/Official/DEV_FW/source/xMasterX/timelapse/gpio_item.h new file mode 100644 index 000000000..5cb2b86c1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/timelapse/gpio_item.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#define GPIO_ITEM_COUNT 8 + +void gpio_item_configure_pin(uint8_t index, GpioMode mode); + +void gpio_item_configure_all_pins(GpioMode mode); + +void gpio_item_set_pin(uint8_t index, bool level); + +void gpio_item_set_all_pins(bool level); + +const char* gpio_item_get_pin_name(uint8_t index); diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/ButtonDown_7x4.png b/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/ButtonDown_7x4.png new file mode 100644 index 000000000..2954bb6a6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/ButtonDown_7x4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/ButtonLeft_4x7.png b/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/ButtonLeft_4x7.png new file mode 100644 index 000000000..0b4655d43 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/ButtonLeft_4x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/ButtonRight_4x7.png b/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/ButtonRight_4x7.png new file mode 100644 index 000000000..8e1c74c1c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/ButtonRight_4x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/ButtonUp_7x4.png b/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/ButtonUp_7x4.png new file mode 100644 index 000000000..1be79328b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/ButtonUp_7x4.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/Pin_star_7x7.png b/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/Pin_star_7x7.png new file mode 100644 index 000000000..42fdea86e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/Pin_star_7x7.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/loading_10px.png b/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/loading_10px.png new file mode 100644 index 000000000..4f626b3d5 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/timelapse/icons/loading_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/zeitraffer.c b/Applications/Official/DEV_FW/source/xMasterX/timelapse/zeitraffer.c new file mode 100644 index 000000000..f70874a8d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/timelapse/zeitraffer.c @@ -0,0 +1,435 @@ +#include +#include +#include +#include +#include +#include +#include "gpio_item.h" +#include "GPIO_Timelapse_icons.h" + +#define CONFIG_FILE_DIRECTORY_PATH "/ext/apps/GPIO" +#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/zeitraffer.conf" + +// Часть кода покрадена из https://github.com/zmactep/flipperzero-hello-world + +int32_t Time = 10; // Таймер +int32_t Count = 10; // Количество кадров +int32_t WorkTime = 0; // Счётчик таймера +int32_t WorkCount = 0; // Счётчик кадров +bool InfiniteShot = false; // Бесконечная съёмка +bool Bulb = false; // Режим BULB +int32_t Backlight = 0; // Подсветка: вкл/выкл/авто +int32_t Delay = 3; // Задержка на отскочить +bool Work = false; + +const NotificationSequence sequence_click = { + &message_note_c7, + &message_delay_50, + &message_sound_off, + NULL, +}; + +typedef enum { + EventTypeTick, + EventTypeInput, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} ZeitrafferEvent; + +static void draw_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + char temp_str[36]; + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + switch(Count) { + case -1: + snprintf(temp_str, sizeof(temp_str), "Set: BULB %li sec", Time); + break; + case 0: + snprintf(temp_str, sizeof(temp_str), "Set: infinite, %li sec", Time); + break; + default: + snprintf(temp_str, sizeof(temp_str), "Set: %li frames, %li sec", Count, Time); + } + canvas_draw_str(canvas, 3, 15, temp_str); + snprintf(temp_str, sizeof(temp_str), "Left: %li frames, %li sec", WorkCount, WorkTime); + canvas_draw_str(canvas, 3, 35, temp_str); + + switch(Backlight) { + case 1: + canvas_draw_str(canvas, 13, 55, "ON"); + break; + case 2: + canvas_draw_str(canvas, 13, 55, "OFF"); + break; + default: + canvas_draw_str(canvas, 13, 55, "AUTO"); + } + + //canvas_draw_icon(canvas, 90, 17, &I_ButtonUp_7x4); + //canvas_draw_icon(canvas, 100, 17, &I_ButtonDown_7x4); + //canvas_draw_icon(canvas, 27, 17, &I_ButtonLeftSmall_3x5); + //canvas_draw_icon(canvas, 37, 17, &I_ButtonRightSmall_3x5); + //canvas_draw_icon(canvas, 3, 48, &I_Pin_star_7x7); + + canvas_draw_icon(canvas, 85, 41, &I_ButtonUp_7x4); + canvas_draw_icon(canvas, 85, 57, &I_ButtonDown_7x4); + canvas_draw_icon(canvas, 59, 48, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 72, 48, &I_ButtonRight_4x7); + canvas_draw_icon(canvas, 3, 48, &I_Pin_star_7x7); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 65, 55, "F"); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 85, 55, "S"); + + canvas_draw_icon(canvas, 59, 48, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 72, 48, &I_ButtonRight_4x7); + + if(Work) { + canvas_draw_icon(canvas, 106, 46, &I_loading_10px); + } +} + +static void input_callback(InputEvent* input_event, void* ctx) { + // Проверяем, что контекст не нулевой + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + + ZeitrafferEvent event = {.type = EventTypeInput, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void timer_callback(FuriMessageQueue* event_queue) { + // Проверяем, что контекст не нулевой + furi_assert(event_queue); + + ZeitrafferEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +int32_t zeitraffer_app(void* p) { + UNUSED(p); + + // Текущее событие типа кастомного типа ZeitrafferEvent + ZeitrafferEvent event; + // Очередь событий на 8 элементов размера ZeitrafferEvent + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(ZeitrafferEvent)); + + // Создаем новый view port + ViewPort* view_port = view_port_alloc(); + // Создаем callback отрисовки, без контекста + view_port_draw_callback_set(view_port, draw_callback, NULL); + // Создаем callback нажатий на клавиши, в качестве контекста передаем + // нашу очередь сообщений, чтоб запихивать в неё эти события + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Создаем GUI приложения + Gui* gui = furi_record_open(RECORD_GUI); + // Подключаем view port к GUI в полноэкранном режиме + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + // Конфигурим пины + gpio_item_configure_all_pins(GpioModeOutputPushPull); + + // Создаем периодический таймер с коллбэком, куда в качестве + // контекста будет передаваться наша очередь событий + FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue); + // Запускаем таймер + //furi_timer_start(timer, 1500); + + // Включаем нотификации + NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + // Загружаем настройки + FlipperFormat* load = flipper_format_file_alloc(storage); + + do { + if(!flipper_format_file_open_existing(load, CONFIG_FILE_PATH)) { + notification_message(notifications, &sequence_error); + break; + } + if(!flipper_format_read_int32(load, "Time", &Time, 1)) { + notification_message(notifications, &sequence_error); + break; + } + if(!flipper_format_read_int32(load, "Count", &Count, 1)) { + notification_message(notifications, &sequence_error); + break; + } + if(!flipper_format_read_int32(load, "Backlight", &Backlight, 1)) { + notification_message(notifications, &sequence_error); + break; + } + if(!flipper_format_read_int32(load, "Delay", &Delay, 1)) { + notification_message(notifications, &sequence_error); + break; + } + notification_message(notifications, &sequence_success); + + } while(0); + + flipper_format_free(load); + + // Бесконечный цикл обработки очереди событий + while(1) { + // Выбираем событие из очереди в переменную event (ждем бесконечно долго, если очередь пуста) + // и проверяем, что у нас получилось это сделать + furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); + + // Наше событие — это нажатие кнопки + if(event.type == EventTypeInput) { + if(event.input.type == InputTypeShort) { // Короткие нажатия + + if(event.input.key == InputKeyBack) { + if(Work) { // Если таймер запущен - нефиг мацать кнопки! + notification_message(notifications, &sequence_error); + } else { + WorkCount = Count; + WorkTime = 3; + if(Count == 0) { + InfiniteShot = true; + WorkCount = 1; + } else + InfiniteShot = false; + + notification_message(notifications, &sequence_success); + } + } + if(event.input.key == InputKeyRight) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Count++; + notification_message(notifications, &sequence_click); + } + } + if(event.input.key == InputKeyLeft) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Count--; + notification_message(notifications, &sequence_click); + } + } + if(event.input.key == InputKeyUp) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Time++; + notification_message(notifications, &sequence_click); + } + } + if(event.input.key == InputKeyDown) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Time--; + notification_message(notifications, &sequence_click); + } + } + if(event.input.key == InputKeyOk) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_click); + furi_timer_stop(timer); + Work = false; + } else { + furi_timer_start(timer, 1000); + Work = true; + + if(WorkCount == 0) WorkCount = Count; + + if(WorkTime == 0) WorkTime = Delay; + + if(Count == 0) { + InfiniteShot = true; + WorkCount = 1; + } else + InfiniteShot = false; + + if(Count == -1) { + gpio_item_set_pin(4, true); + gpio_item_set_pin(5, true); + Bulb = true; + WorkCount = 1; + WorkTime = Time; + } else + Bulb = false; + + notification_message(notifications, &sequence_success); + } + } + } + if(event.input.type == InputTypeLong) { // Длинные нажатия + // Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения + if(event.input.key == InputKeyBack) { + if(furi_timer_is_running(timer)) { // А если работает таймер - не выходим :D + notification_message(notifications, &sequence_error); + } else { + notification_message(notifications, &sequence_click); + gpio_item_set_all_pins(false); + furi_timer_stop(timer); + notification_message( + notifications, &sequence_display_backlight_enforce_auto); + break; + } + } + if(event.input.key == InputKeyOk) { + // Нам ваша подсветка и нахой не нужна! Или нужна? + Backlight++; + if(Backlight > 2) Backlight = 0; + } + } + + if(event.input.type == InputTypeRepeat) { // Зажатые кнопки + if(event.input.key == InputKeyRight) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Count = Count + 10; + } + } + if(event.input.key == InputKeyLeft) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Count = Count - 10; + } + } + if(event.input.key == InputKeyUp) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Time = Time + 10; + } + } + if(event.input.key == InputKeyDown) { + if(furi_timer_is_running(timer)) { + notification_message(notifications, &sequence_error); + } else { + Time = Time - 10; + } + } + } + } + + // Наше событие — это сработавший таймер + else if(event.type == EventTypeTick) { + WorkTime--; + + if(WorkTime < 1) { // фоткаем + notification_message(notifications, &sequence_blink_white_100); + if(Bulb) { + gpio_item_set_all_pins(false); + WorkCount = 0; + } else { + WorkCount--; + view_port_update(view_port); + notification_message(notifications, &sequence_click); + // Дрыгаем ногами + //gpio_item_set_all_pins(true); + gpio_item_set_pin(4, true); + gpio_item_set_pin(5, true); + furi_delay_ms(400); // На короткие нажатия фотик плохо реагирует + gpio_item_set_pin(4, false); + gpio_item_set_pin(5, false); + //gpio_item_set_all_pins(false); + + if(InfiniteShot) WorkCount++; + + WorkTime = Time; + view_port_update(view_port); + } + } else { + // Отправляем нотификацию мигания синим светодиодом + notification_message(notifications, &sequence_blink_blue_100); + } + + if(WorkCount < 1) { // закончили + Work = false; + gpio_item_set_all_pins(false); + furi_timer_stop(timer); + notification_message(notifications, &sequence_audiovisual_alert); + WorkTime = 3; + WorkCount = 0; + } + + switch(Backlight) { // чо по подсветке? + case 1: + notification_message(notifications, &sequence_display_backlight_on); + break; + case 2: + notification_message(notifications, &sequence_display_backlight_off); + break; + default: + notification_message(notifications, &sequence_display_backlight_enforce_auto); + } + } + if(Time < 1) Time = 1; // Не даём открутить таймер меньше единицы + if(Count < -1) + Count = 0; // А тут даём, бо 0 кадров это бесконечная съёмка, а -1 кадров - BULB + } + + // Схороняем настройки + FlipperFormat* save = flipper_format_file_alloc(storage); + + do { + if(!flipper_format_file_open_always(save, CONFIG_FILE_PATH)) { + notification_message(notifications, &sequence_error); + break; + } + if(!flipper_format_write_header_cstr(save, "Zeitraffer", 1)) { + notification_message(notifications, &sequence_error); + break; + } + if(!flipper_format_write_comment_cstr( + save, + "Zeitraffer app settings: n of frames, interval time, backlight type, Delay")) { + notification_message(notifications, &sequence_error); + break; + } + if(!flipper_format_write_int32(save, "Time", &Time, 1)) { + notification_message(notifications, &sequence_error); + break; + } + if(!flipper_format_write_int32(save, "Count", &Count, 1)) { + notification_message(notifications, &sequence_error); + break; + } + if(!flipper_format_write_int32(save, "Backlight", &Backlight, 1)) { + notification_message(notifications, &sequence_error); + break; + } + if(!flipper_format_write_int32(save, "Delay", &Delay, 1)) { + notification_message(notifications, &sequence_error); + break; + } + + } while(0); + + flipper_format_free(save); + + furi_record_close(RECORD_STORAGE); + + // Очищаем таймер + furi_timer_free(timer); + + // Специальная очистка памяти, занимаемой очередью + furi_message_queue_free(event_queue); + + // Чистим созданные объекты, связанные с интерфейсом + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_record_close(RECORD_GUI); + + // Очищаем нотификации + furi_record_close(RECORD_NOTIFICATION); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/timelapse/zeitraffer.png b/Applications/Official/DEV_FW/source/xMasterX/timelapse/zeitraffer.png new file mode 100644 index 000000000..3a42e406d Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/timelapse/zeitraffer.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/.gitignore b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/.gitignore new file mode 100644 index 000000000..e4e5f6c8b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/.gitignore @@ -0,0 +1 @@ +*~ \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/CHANGELOG.md b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/CHANGELOG.md new file mode 100644 index 000000000..d0924edd3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +## 0.4 +- Show active/inactive state in primary font (bold) + +## 0.3 +- Add a delay between key-presses (with left/right buttons) + +## 0.2 +- Update icon + +## 0.1 +- Initial release of the USB HID Autofire application diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/README.md b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/README.md new file mode 100644 index 000000000..b2cbd7606 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/README.md @@ -0,0 +1,32 @@ +# USB HID Autofire + +[GitHub](https://github.com/pbek/usb_hid_autofire) | +[Latest release](https://github.com/pbek/usb_hid_autofire/releases/latest) | +[Changelog](CHANGELOG.md) | +[License](LICENSE.md) + +[![Build](https://github.com/pbek/usb_hid_autofire/actions/workflows/build-test.yml/badge.svg)](https://github.com/pbek/usb_hid_autofire/actions/workflows/build-test.yml) + +This is a simple Flipper Zero application to send left-clicks as a USB HID device. + +![Screenshot](screenshot.png) + +## Installation + +Download the [latest release](https://github.com/pbek/usb_hid_autofire/releases/latest) +of the *fap* file and put it into the `apps` folder on your SD card of your Flipper Zero. + +## Building + +```shell +cd applications_user +git clone https://github.com/pbek/usb_hid_autofire.git + +cd .. + +# Build the application +./fbt fap_usb_hid_autofire + +# Build and launch the application +./fbt launch_app APPSRC=usb_hid_autofire +``` diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/application.fam b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/application.fam new file mode 100644 index 000000000..56762838a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/application.fam @@ -0,0 +1,13 @@ +App( + appid="usb_hid_autofire", + name="USB HID Autofire", + apptype=FlipperAppType.EXTERNAL, + entry_point="usb_hid_autofire_app", + cdefines=["APP_USB_HID_AUTOFIRE"], + requires=[ + "gui", + ], + stack_size=1 * 1024, + fap_icon="usb_hid_autofire.png", + fap_category="Misc_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/screenshot.png b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/screenshot.png new file mode 100644 index 000000000..b5547e1ba Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/screenshot.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/tools.c b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/tools.c new file mode 100644 index 000000000..2b452b55b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/tools.c @@ -0,0 +1,56 @@ +// +// Tools for USB HID Autofire +// + +void strrev(char* arr, int start, int end) { + char temp; + + if (start >= end) + return; + + temp = *(arr + start); + *(arr + start) = *(arr + end); + *(arr + end) = temp; + + start++; + end--; + strrev(arr, start, end); +} + +char *itoa(int number, char *arr, int base) +{ + int i = 0, r, negative = 0; + + if (number == 0) + { + arr[i] = '0'; + arr[i + 1] = '\0'; + return arr; + } + + if (number < 0 && base == 10) + { + number *= -1; + negative = 1; + } + + while (number != 0) + { + r = number % base; + arr[i] = (r > 9) ? (r - 10) + 'a' : r + '0'; + i++; + number /= base; + } + + if (negative) + { + arr[i] = '-'; + i++; + } + + strrev(arr, 0, i - 1); + + arr[i] = '\0'; + + return arr; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/tools.h b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/tools.h new file mode 100644 index 000000000..9c71ea6ca --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/tools.h @@ -0,0 +1,7 @@ +#ifndef FLIPPERZERO_FIRMWARE_TOOLS_H +#define FLIPPERZERO_FIRMWARE_TOOLS_H + +void strrev(char *arr, int start, int end); +char *itoa(int number, char *arr, int base); + +#endif //FLIPPERZERO_FIRMWARE_TOOLS_H diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/usb_hid_autofire.c b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/usb_hid_autofire.c new file mode 100644 index 000000000..1333ba94a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/usb_hid_autofire.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include +#include "version.h" +#include "tools.h" + +// Uncomment to be able to make a screenshot +//#define USB_HID_AUTOFIRE_SCREENSHOT + +typedef enum { + EventTypeInput, +} EventType; + +typedef struct { + union { + InputEvent input; + }; + EventType type; +} UsbMouseEvent; + +bool btn_left_autofire = false; +uint32_t autofire_delay = 10; + +static void usb_hid_autofire_render_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + char autofire_delay_str[12]; + //std::string pi = "pi is " + std::to_string(3.1415926); + itoa(autofire_delay, autofire_delay_str, 10); + //sprintf(autofire_delay_str, "%lu", autofire_delay); + + canvas_clear(canvas); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "USB HID Autofire"); + canvas_draw_str(canvas, 0, 34, btn_left_autofire ? "" : ""); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 90, 10, "v"); + canvas_draw_str(canvas, 96, 10, VERSION); + canvas_draw_str(canvas, 0, 22, "Press [ok] for auto left clicking"); + canvas_draw_str(canvas, 0, 46, "delay [ms]:"); + canvas_draw_str(canvas, 50, 46, autofire_delay_str); + canvas_draw_str(canvas, 0, 63, "Press [back] to exit"); +} + +static void usb_hid_autofire_input_callback(InputEvent* input_event, void* ctx) { + FuriMessageQueue* event_queue = ctx; + + UsbMouseEvent event; + event.type = EventTypeInput; + event.input = *input_event; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +int32_t usb_hid_autofire_app(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent)); + furi_check(event_queue); + ViewPort* view_port = view_port_alloc(); + + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); +#ifndef USB_HID_AUTOFIRE_SCREENSHOT + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); +#endif + + view_port_draw_callback_set(view_port, usb_hid_autofire_render_callback, NULL); + view_port_input_callback_set(view_port, usb_hid_autofire_input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + UsbMouseEvent event; + while(1) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 50); + + if(event_status == FuriStatusOk) { + if(event.type == EventTypeInput) { + if(event.input.key == InputKeyBack) { + break; + } + + if(event.input.type != InputTypeRelease) { + continue; + } + + switch(event.input.key) { + case InputKeyOk: + btn_left_autofire = !btn_left_autofire; + break; + case InputKeyLeft: + if(autofire_delay > 0) { + autofire_delay -= 10; + } + break; + case InputKeyRight: + autofire_delay += 10; + break; + default: + break; + } + } + } + + if(btn_left_autofire) { + furi_hal_hid_mouse_press(HID_MOUSE_BTN_LEFT); + // TODO: Don't wait, but use the timer directly to just don't send the release event (see furi_hal_cortex_delay_us) + furi_delay_us(autofire_delay * 500); + furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); + furi_delay_us(autofire_delay * 500); + } + + view_port_update(view_port); + } + + furi_hal_usb_set_config(usb_mode_prev, NULL); + + // remove & free all stuff created by app + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/usb_hid_autofire.kra b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/usb_hid_autofire.kra new file mode 100644 index 000000000..21d416548 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/usb_hid_autofire.kra differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/usb_hid_autofire.png b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/usb_hid_autofire.png new file mode 100644 index 000000000..369bff022 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/usb_hid_autofire.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/usb_hid_autofire.svg b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/usb_hid_autofire.svg new file mode 100644 index 000000000..ed66f3cfd --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/usb_hid_autofire.svg @@ -0,0 +1,75 @@ + + + + + + + + + A + + diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/version.h b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/version.h new file mode 100644 index 000000000..ac1f5d0fa --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_hid_autofire/version.h @@ -0,0 +1 @@ +#define VERSION "0.4" diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/.gitattributes b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/.gitattributes new file mode 100644 index 000000000..dfe077042 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/.gitignore b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/.gitignore new file mode 100644 index 000000000..c6127b38c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/application.fam b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/application.fam new file mode 100644 index 000000000..cf2f0be6e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/application.fam @@ -0,0 +1,14 @@ +App( + appid="USB_Midi", + name="USB Midi", + apptype=FlipperAppType.PLUGIN, + entry_point="usb_midi_app", + requires=[ + "gui", + ], + stack_size=4 * 1024, + order=20, + fap_icon="usb_midi.png", + fap_category="Music_Extra", + # fap_icon_assets="icons", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/config.h b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/config.h new file mode 100644 index 000000000..c62c1b1ef --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/config.h @@ -0,0 +1,3 @@ +#include + +#define SYSEX_BUFFER_LEN 16 \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/message.c b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/message.c new file mode 100644 index 000000000..7bee9816a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/message.c @@ -0,0 +1,144 @@ +#include "message.h" + +/** Returns the data within the MidiEvent as a NoteOffEvent struct */ +NoteOffEvent AsNoteOff(MidiEvent* event) { + NoteOffEvent m; + m.channel = event->channel; + m.note = event->data[0]; + m.velocity = event->data[1]; + return m; +} + +/** Returns the data within the MidiEvent as a NoteOnEvent struct */ +NoteOnEvent AsNoteOn(MidiEvent* event) { + NoteOnEvent m; + m.channel = event->channel; + m.note = event->data[0]; + m.velocity = event->data[1]; + return m; +} + +/** Returns the data within the MidiEvent as a PolyphonicKeyPressureEvent struct */ +PolyphonicKeyPressureEvent AsPolyphonicKeyPressure(MidiEvent* event) { + PolyphonicKeyPressureEvent m; + m.channel = event->channel; + m.note = event->data[0]; + m.pressure = event->data[1]; + return m; +} + +/** Returns the data within the MidiEvent as a ControlChangeEvent struct.*/ +ControlChangeEvent AsControlChange(MidiEvent* event) { + ControlChangeEvent m; + m.channel = event->channel; + m.control_number = event->data[0]; + m.value = event->data[1]; + return m; +} + +/** Returns the data within the MidiEvent as a ProgramChangeEvent struct.*/ +ProgramChangeEvent AsProgramChange(MidiEvent* event) { + ProgramChangeEvent m; + m.channel = event->channel; + m.program = event->data[0]; + return m; +} + +/** Returns the data within the MidiEvent as a ProgramChangeEvent struct.*/ +ChannelPressureEvent AsChannelPressure(MidiEvent* event) { + ChannelPressureEvent m; + m.channel = event->channel; + m.pressure = event->data[0]; + return m; +} + +/** Returns the data within the MidiEvent as a PitchBendEvent struct.*/ +PitchBendEvent AsPitchBend(MidiEvent* event) { + PitchBendEvent m; + m.channel = event->channel; + m.value = ((uint16_t)(event->data[1]) << 7) + (event->data[0] - 8192); + return m; +} + +SystemExclusiveEvent AsSystemExclusive(MidiEvent* event) { + SystemExclusiveEvent m; + m.length = event->sysex_message_len; + for(int i = 0; i < SYSEX_BUFFER_LEN; i++) { + m.data[i] = 0; + if(i < m.length) { + m.data[i] = event->sysex_data[i]; + } + } + return m; +} + +MTCQuarterFrameEvent AsMTCQuarterFrame(MidiEvent* event) { + MTCQuarterFrameEvent m; + m.message_type = (event->data[0] & 0x70) >> 4; + m.value = (event->data[0]) & 0x0f; + return m; +} + +SongPositionPointerEvent AsSongPositionPointer(MidiEvent* event) { + SongPositionPointerEvent m; + m.position = ((uint16_t)(event->data[1]) << 7) | (event->data[0]); + return m; +} + +SongSelectEvent AsSongSelect(MidiEvent* event) { + SongSelectEvent m; + m.song = event->data[0]; + return m; +} + +AllSoundOffEvent AsAllSoundOff(MidiEvent* event) { + AllSoundOffEvent m; + m.channel = event->channel; + return m; +} + +ResetAllControllersEvent AsResetAllControllers(MidiEvent* event) { + ResetAllControllersEvent m; + m.channel = event->channel; + m.value = event->data[1]; + return m; +} + +LocalControlEvent AsLocalControl(MidiEvent* event) { + LocalControlEvent m; + m.channel = event->channel; + m.local_control_off = (event->data[1] == 0); + m.local_control_on = (event->data[1] == 127); + return m; +} + +AllNotesOffEvent AsAllNotesOff(MidiEvent* event) { + AllNotesOffEvent m; + m.channel = event->channel; + return m; +} + +OmniModeOffEvent AsOmniModeOff(MidiEvent* event) { + OmniModeOffEvent m; + m.channel = event->channel; + return m; +} + +OmniModeOnEvent AsOmniModeOn(MidiEvent* event) { + OmniModeOnEvent m; + m.channel = event->channel; + return m; +} + +MonoModeOnEvent AsMonoModeOn(MidiEvent* event) { + MonoModeOnEvent m; + m.channel = event->channel; + m.num_channels = event->data[1]; + return m; +} + +PolyModeOnEvent AsPolyModeOn(MidiEvent* event) { + PolyModeOnEvent m; + m.channel = event->channel; + return m; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/message.h b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/message.h new file mode 100644 index 000000000..88402c4a4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/message.h @@ -0,0 +1,251 @@ +#pragma once +#include +#include +#include "config.h" + +typedef enum { + NoteOff, /**< & */ + NoteOn, /**< & */ + PolyphonicKeyPressure, /**< & */ + ControlChange, /**< & */ + ProgramChange, /**< & */ + ChannelPressure, /**< & */ + PitchBend, /**< & */ + SystemCommon, /**< & */ + SystemRealTime, /**< & */ + ChannelMode, /**< & */ + MessageLast, /**< & */ +} MidiMessageType; + +typedef enum { + SystemExclusive, /**< & */ + MTCQuarterFrame, /**< & */ + SongPositionPointer, /**< & */ + SongSelect, /**< & */ + SCUndefined0, /**< & */ + SCUndefined1, /**< & */ + TuneRequest, /**< & */ + SysExEnd, /**< & */ + SystemCommonLast, /**< & */ +} SystemCommonType; + +typedef enum { + TimingClock, /**< & */ + SRTUndefined0, /**< & */ + Start, /**< & */ + Continue, /**< & */ + Stop, /**< & */ + SRTUndefined1, /**< & */ + ActiveSensing, /**< & */ + Reset, /**< & */ + SystemRealTimeLast, /**< & */ +} SystemRealTimeType; + +typedef enum { + AllSoundOff, /**< & */ + ResetAllControllers, /**< & */ + LocalControl, /**< & */ + AllNotesOff, /**< & */ + OmniModeOff, /**< & */ + OmniModeOn, /**< & */ + MonoModeOn, /**< & */ + PolyModeOn, /**< & */ + ChannelModeLast, /**< & */ +} ChannelModeType; + +/** Struct containing note, and velocity data for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t note; /**< & */ + uint8_t velocity; /**< & */ +} NoteOffEvent; + +/** Struct containing note, and velocity data for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t note; /**< & */ + uint8_t velocity; /**< & */ +} NoteOnEvent; + +/** Struct containing note, and pressure data for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; + uint8_t note; + uint8_t pressure; +} PolyphonicKeyPressureEvent; + +/** Struct containing control number, and value for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t control_number; /**< & */ + uint8_t value; /**< & */ +} ControlChangeEvent; + +/** Struct containing new program number, for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t program; /**< & */ +} ProgramChangeEvent; + +/** Struct containing pressure (aftertouch), for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t pressure; /**< & */ +} ChannelPressureEvent; + +/** Struct containing pitch bend value for a given channel. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + int16_t value; /**< & */ +} PitchBendEvent; + +/** Struct containing sysex data. +Can be made from MidiEvent +*/ +typedef struct { + int length; + uint8_t data[SYSEX_BUFFER_LEN]; /**< & */ +} SystemExclusiveEvent; + +/** Struct containing QuarterFrame data. +Can be made from MidiEvent +*/ +typedef struct { + uint8_t message_type; /**< & */ + uint8_t value; /**< & */ +} MTCQuarterFrameEvent; + +/** Struct containing song position data. +Can be made from MidiEvent +*/ +typedef struct { + uint16_t position; /**< & */ +} SongPositionPointerEvent; + +/** Struct containing song select data. +Can be made from MidiEvent +*/ +typedef struct { + uint8_t song; /**< & */ +} SongSelectEvent; + +/** Struct containing sound off data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ +} AllSoundOffEvent; + +/** Struct containing ResetAllControllersEvent data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t value; /**< & */ +} ResetAllControllersEvent; + +/** Struct containing LocalControlEvent data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + bool local_control_off; /**< & */ + bool local_control_on; /**< & */ +} LocalControlEvent; + +/** Struct containing AllNotesOffEvent data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ +} AllNotesOffEvent; + +/** Struct containing OmniModeOffEvent data. + * Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ +} OmniModeOffEvent; + +/** Struct containing OmniModeOnEvent data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ +} OmniModeOnEvent; + +/** Struct containing MonoModeOnEvent data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ + uint8_t num_channels; /**< & */ +} MonoModeOnEvent; + +/** Struct containing PolyModeOnEvent data. +Can be made from MidiEvent +*/ +typedef struct { + int channel; /**< & */ +} PolyModeOnEvent; + +/** Simple MidiEvent with message type, channel, and data[2] members. +*/ +typedef struct { + MidiMessageType type; + int channel; + uint8_t data[2]; + uint8_t sysex_data[SYSEX_BUFFER_LEN]; + uint8_t sysex_message_len; + SystemCommonType sc_type; + SystemRealTimeType srt_type; + ChannelModeType cm_type; +} MidiEvent; + +/** Returns the data within the MidiEvent as a NoteOffEvent struct */ +NoteOffEvent AsNoteOff(MidiEvent* event); + +/** Returns the data within the MidiEvent as a NoteOnEvent struct */ +NoteOnEvent AsNoteOn(MidiEvent* event); + +/** Returns the data within the MidiEvent as a PolyphonicKeyPressureEvent struct */ +PolyphonicKeyPressureEvent AsPolyphonicKeyPressure(MidiEvent* event); + +/** Returns the data within the MidiEvent as a ControlChangeEvent struct.*/ +ControlChangeEvent AsControlChange(MidiEvent* event); + +/** Returns the data within the MidiEvent as a ProgramChangeEvent struct.*/ +ProgramChangeEvent AsProgramChange(MidiEvent* event); + +/** Returns the data within the MidiEvent as a ProgramChangeEvent struct.*/ +ChannelPressureEvent AsChannelPressure(MidiEvent* event); + +/** Returns the data within the MidiEvent as a PitchBendEvent struct.*/ +PitchBendEvent AsPitchBend(MidiEvent* event); + +SystemExclusiveEvent AsSystemExclusive(MidiEvent* event); +MTCQuarterFrameEvent AsMTCQuarterFrame(MidiEvent* event); +SongPositionPointerEvent AsSongPositionPointer(MidiEvent* event); +SongSelectEvent AsSongSelect(MidiEvent* event); +AllSoundOffEvent AsAllSoundOff(MidiEvent* event); +ResetAllControllersEvent AsResetAllControllers(MidiEvent* event); +LocalControlEvent AsLocalControl(MidiEvent* event); +AllNotesOffEvent AsAllNotesOff(MidiEvent* event); +OmniModeOffEvent AsOmniModeOff(MidiEvent* event); +OmniModeOnEvent AsOmniModeOn(MidiEvent* event); +MonoModeOnEvent AsMonoModeOn(MidiEvent* event); +PolyModeOnEvent AsPolyModeOn(MidiEvent* event); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/parser.c b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/parser.c new file mode 100644 index 000000000..86120e007 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/parser.c @@ -0,0 +1,149 @@ +#include +#include "parser.h" + +typedef enum { + ParserEmpty, + ParserHasStatus, + ParserHasData0, + ParserSysEx, +} ParserState; + +const uint8_t kStatusByteMask = 0x80; +const uint8_t kMessageMask = 0x70; +const uint8_t kDataByteMask = 0x7F; +const uint8_t kSystemCommonMask = 0xF0; +const uint8_t kChannelMask = 0x0F; +const uint8_t kRealTimeMask = 0xF8; +const uint8_t kSystemRealTimeMask = 0x07; + +struct MidiParser { + MidiMessageType status; + ParserState state; + MidiEvent incoming_message; +}; + +MidiParser* midi_parser_alloc(void) { + MidiParser* parser = malloc(sizeof(MidiParser)); + parser->incoming_message.type = MessageLast; + parser->state = ParserEmpty; + return parser; +} + +void midi_parser_free(MidiParser* parser) { + free(parser); +} + +bool midi_parser_parse(MidiParser* parser, uint8_t byte) { + bool parsed = false; + MidiEvent* event = &parser->incoming_message; + + switch(parser->state) { + case ParserEmpty: + // check byte for valid Status Byte + if(byte & kStatusByteMask) { + // Get MessageType, and Channel + event->channel = byte & kChannelMask; + event->type = (MidiMessageType)((byte & kMessageMask) >> 4); + + // Validate, and move on. + if(event->type < MessageLast) { + parser->state = ParserHasStatus; + // Mark this status byte as running_status + parser->status = event->type; + + if(parser->status == SystemCommon) { + event->channel = 0; + //system real time = 1111 1xxx + if(byte & 0x08) { + event->type = SystemRealTime; + parser->status = SystemRealTime; + event->srt_type = (SystemRealTimeType)(byte & kSystemRealTimeMask); + + //short circuit to start + parser->state = ParserEmpty; + //queue_.push(incoming_message_); + parsed = true; + } + //system common + else { + event->sc_type = (SystemCommonType)(byte & 0x07); + //sysex + if(event->sc_type == SystemExclusive) { + parser->state = ParserSysEx; + event->sysex_message_len = 0; + } + //short circuit + else if(event->sc_type > SongSelect) { + parser->state = ParserEmpty; + //queue_.push(incoming_message_); + parsed = true; + } + } + } + } + // Else we'll keep waiting for a valid incoming status byte + } else { + // Handle as running status + event->type = parser->status; + event->data[0] = byte & kDataByteMask; + parser->state = ParserHasData0; + } + break; + case ParserHasStatus: + if((byte & kStatusByteMask) == 0) { + event->data[0] = byte & kDataByteMask; + if(parser->status == ChannelPressure || parser->status == ProgramChange || + event->sc_type == MTCQuarterFrame || event->sc_type == SongSelect) { + //these are just one data byte, so we short circuit back to start + parser->state = ParserEmpty; + //queue_.push(incoming_message_); + parsed = true; + } else { + parser->state = ParserHasData0; + } + + //ChannelModeMessages (reserved Control Changes) + if(parser->status == ControlChange && event->data[0] > 119) { + event->type = ChannelMode; + parser->status = ChannelMode; + event->cm_type = (ChannelModeType)(event->data[0] - 120); + } + } else { + // invalid message go back to start ;p + parser->state = ParserEmpty; + } + break; + case ParserHasData0: + if((byte & kStatusByteMask) == 0) { + event->data[1] = byte & kDataByteMask; + // At this point the message is valid, and we can add this MidiEvent to the queue + //queue_.push(incoming_message_); + parsed = true; + } + // Regardless, of whether the data was valid or not we go back to empty + // because either the message is queued for handling or its not. + parser->state = ParserEmpty; + break; + case ParserSysEx: + // end of sysex + if(byte == 0xf7) { + parser->state = ParserEmpty; + //queue_.push(incoming_message_); + parsed = true; + } else { + if(event->sysex_message_len < SYSEX_BUFFER_LEN) { + event->sysex_data[event->sysex_message_len] = byte; + event->sysex_message_len++; + } + } + break; + default: + break; + } + + return parsed; +} + +MidiEvent* midi_parser_get_message(MidiParser* parser) { + return &parser->incoming_message; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/parser.h b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/parser.h new file mode 100644 index 000000000..93630f026 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/parser.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include "message.h" + +typedef struct MidiParser MidiParser; + +MidiParser* midi_parser_alloc(void); + +void midi_parser_free(MidiParser* parser); + +bool midi_parser_parse(MidiParser* parser, uint8_t data); + +MidiEvent* midi_parser_get_message(MidiParser* parser); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/usb_message.c b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/usb_message.c new file mode 100644 index 000000000..b6844c5f4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/usb_message.c @@ -0,0 +1,40 @@ +#include "usb_message.h" + +CodeIndex code_index_from_data(uint8_t data) { + return (CodeIndex)(data & 0b00001111); +} + +uint8_t cable_id_from_data(uint8_t data) { + return (data >> 4) & 0b00001111; +} + +uint8_t usb_message_data_size(CodeIndex code_index) { + uint8_t data_size = 0; + switch(code_index) { + case CodeIndexCommon1Byte: + /* case CodeIndexSysExEnd1Byte: */ + case CodeIndexSingleByte: + data_size = 1; + break; + case CodeIndexSysEx2Byte: + case CodeIndexSysExEnd2Byte: + case CodeIndexProgramChange: + case CodeIndexChannelPressure: + data_size = 2; + break; + case CodeIndexSysEx3Byte: + case CodeIndexSysExStart: + case CodeIndexSysExEnd3Byte: + case CodeIndexNoteOff: + case CodeIndexNoteOn: + case CodeIndexPolyKeyPress: + case CodeIndexControlChange: + case CodeIndexPitchBendChange: + data_size = 3; + break; + default: + break; + } + + return data_size; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/usb_message.h b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/usb_message.h new file mode 100644 index 000000000..852e9cb4f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/midi/usb_message.h @@ -0,0 +1,28 @@ +#pragma once +#include + +typedef enum { + CodeIndexMisc = 0x0, /**< Reserved, MIDI Size: 1, 2, 3 */ + CodeIndexCableEvent = 0x1, /**< Reserved, MIDI Size: 1, 2, 3 */ + CodeIndexSysEx2Byte = 0x2, /**< MIDI Size: 2 */ + CodeIndexSysEx3Byte = 0x3, /**< MIDI Size: 3 */ + CodeIndexSysExStart = 0x4, /**< MIDI Size: 3 */ + CodeIndexCommon1Byte = 0x5, /**< MIDI Size: 1 */ + CodeIndexSysExEnd1Byte = 0x5, /**< MIDI Size: 1 */ + CodeIndexSysExEnd2Byte = 0x6, /**< MIDI Size: 2 */ + CodeIndexSysExEnd3Byte = 0x7, /**< MIDI Size: 3 */ + CodeIndexNoteOff = 0x8, /**< MIDI Size: 3 */ + CodeIndexNoteOn = 0x9, /**< MIDI Size: 3 */ + CodeIndexPolyKeyPress = 0xA, /**< MIDI Size: 3 */ + CodeIndexControlChange = 0xB, /**< MIDI Size: 3 */ + CodeIndexProgramChange = 0xC, /**< MIDI Size: 2 */ + CodeIndexChannelPressure = 0xD, /**< MIDI Size: 2 */ + CodeIndexPitchBendChange = 0xE, /**< MIDI Size: 3 */ + CodeIndexSingleByte = 0xF, /**< MIDI Size: 1 */ +} CodeIndex; + +CodeIndex code_index_from_data(uint8_t data); + +uint8_t cable_id_from_data(uint8_t data); + +uint8_t usb_message_data_size(CodeIndex code_index); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb/cm3_usb_audio.h b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb/cm3_usb_audio.h new file mode 100644 index 000000000..3c767f929 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb/cm3_usb_audio.h @@ -0,0 +1,234 @@ +/** @defgroup usb_audio_defines USB Audio Type Definitions + +@brief Defined Constants and Types for the USB Audio Type Definitions + +@ingroup USB_defines + +@version 1.0.0 + +@author @htmlonly © @endhtmlonly 2014 +Daniel Thompson +Seb Holzapfel + +@date 19 April 2014 + +LGPL License Terms @ref lgpl_license +*/ + +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 Daniel Thompson + * Copyright (C) 2018 Seb Holzapfel + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/**@{*/ + +#ifndef LIBOPENCM3_USB_AUDIO_H +#define LIBOPENCM3_USB_AUDIO_H + +#include + +/* + * Definitions from the USB_AUDIO_ or usb_audio_ namespace come from: + * "Universal Serial Bus Class Definitions for Audio Devices, Revision 1.0" + */ + +/* Table A-1: Audio Interface Class Code */ +#define USB_CLASS_AUDIO 0x01 + +/* Table A-2: Audio Interface Subclass Codes */ +#define USB_AUDIO_SUBCLASS_UNDEFINED 0x00 +#define USB_AUDIO_SUBCLASS_CONTROL 0x01 +#define USB_AUDIO_SUBCLASS_AUDIOSTREAMING 0x02 +#define USB_AUDIO_SUBCLASS_MIDISTREAMING 0x03 + +/* Table A-4: Audio Class-specific Descriptor Types */ +#define USB_AUDIO_DT_CS_UNDEFINED 0x20 +#define USB_AUDIO_DT_CS_DEVICE 0x21 +#define USB_AUDIO_DT_CS_CONFIGURATION 0x22 +#define USB_AUDIO_DT_CS_STRING 0x23 +#define USB_AUDIO_DT_CS_INTERFACE 0x24 +#define USB_AUDIO_DT_CS_ENDPOINT 0x25 + +/* Table A-5: Audio Class-Specific AC Interface Descriptor Subtypes */ +#define USB_AUDIO_TYPE_AC_DESCRIPTOR_UNDEFINED 0x00 +#define USB_AUDIO_TYPE_HEADER 0x01 +#define USB_AUDIO_TYPE_INPUT_TERMINAL 0x02 +#define USB_AUDIO_TYPE_OUTPUT_TERMINAL 0x03 +#define USB_AUDIO_TYPE_MIXER_UNIT 0x04 +#define USB_AUDIO_TYPE_SELECTOR_UNIT 0x05 +#define USB_AUDIO_TYPE_FEATURE_UNIT 0x06 +#define USB_AUDIO_TYPE_PROCESSING_UNIT 0x07 +#define USB_AUDIO_TYPE_EXTENSION_UNIT 0x08 + +/* Table 4-2: Class-Specific AC Interface Header Descriptor (head) */ +struct usb_audio_header_descriptor_head { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint16_t bcdADC; + uint16_t wTotalLength; + uint8_t bInCollection; + /* ... */ +} __attribute__((packed)); + +/* Table 4-2: Class-Specific AC Interface Header Descriptor (body) */ +struct usb_audio_header_descriptor_body { + /* ... */ + uint8_t baInterfaceNr; +} __attribute__((packed)); + +/* Table 4-3: Input Terminal Descriptor */ +struct usb_audio_input_terminal_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bTerminalID; + uint16_t wTerminalType; + uint8_t bAssocTerminal; + uint8_t bNrChannels; + uint16_t wChannelConfig; + uint8_t iChannelNames; + uint8_t iTerminal; +} __attribute__((packed)); + +/* Table 4-3: Output Terminal Descriptor */ +struct usb_audio_output_terminal_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bTerminalID; + uint16_t wTerminalType; + uint8_t bAssocTerminal; + uint8_t bSourceID; + uint8_t iTerminal; +} __attribute__((packed)); + +/* Table 4-7: Feature Unit Descriptor (head) */ +struct usb_audio_feature_unit_descriptor_head { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bUnitID; + uint8_t bSourceID; + uint8_t bControlSize; + uint16_t bmaControlMaster; /* device can assume 16-bit, given highest + * defined bit in spec is bit #9. + * (it is thus required bControlSize=2) */ + /* ... */ +} __attribute__((packed)); + +/* Table 4-7: Feature Unit Descriptor (body) */ +struct usb_audio_feature_unit_descriptor_body { + /* ... */ + uint16_t bmaControl; + /* ... */ +} __attribute__((packed)); + +/* Table 4-7: Feature Unit Descriptor (tail) */ +struct usb_audio_feature_unit_descriptor_tail { + /* ... */ + uint8_t iFeature; +} __attribute__((packed)); + +/* Table 4-7: Feature Unit Descriptor (2-channel) + * + * This structure is a convenience covering the (common) case where + * there are 2 channels associated with the feature unit + */ +struct usb_audio_feature_unit_descriptor_2ch { + struct usb_audio_feature_unit_descriptor_head head; + struct usb_audio_feature_unit_descriptor_body channel_control[2]; + struct usb_audio_feature_unit_descriptor_tail tail; +} __attribute__((packed)); + +/* Table 4-19: Class-Specific AS Interface Descriptor */ +struct usb_audio_stream_interface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bTerminalLink; + uint8_t bDelay; + uint16_t wFormatTag; +} __attribute__((packed)); + +/* Table 4-20: Standard AS Isochronous Audio Data Endpoint Descriptor */ +struct usb_audio_stream_endpoint_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + uint8_t bRefresh; + uint8_t bSynchAddress; +} __attribute__((packed)); + +/* Table 4-21: Class-Specific AS Isochronous Audio Data Endpoint Descriptor */ +struct usb_audio_stream_audio_endpoint_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bmAttributes; + uint8_t bLockDelayUnits; + uint16_t wLockDelay; +} __attribute__((packed)); + +/* + * Definitions from the USB_AUDIO_FORMAT_ or usb_audio_format_ namespace come from: + * "Universal Serial Bus Device Class Definition for Audio Data Formats, Revision 1.0" + */ + +/* Table 2-1: Type I Format Type Descriptor (head) */ +struct usb_audio_format_type1_descriptor_head { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bFormatType; + uint8_t bNrChannels; + uint8_t bSubFrameSize; + uint8_t bBitResolution; + uint8_t bSamFreqType; + /* ... */ +} __attribute__((packed)); + +/* Table 2-2: Continuous Sampling Frequency */ +struct usb_audio_format_continuous_sampling_frequency { + /* ... */ + uint32_t tLowerSamFreq : 24; + uint32_t tUpperSamFreq : 24; +} __attribute__((packed)); + +/* Table 2-3: Discrete Number of Sampling Frequencies */ +struct usb_audio_format_discrete_sampling_frequency { + /* ... */ + uint32_t tSamFreq : 24; +} __attribute__((packed)); + +/* Table 2-1: Type I Format Type Descriptor (1 sampling frequency) + * + * This structure is a convenience covering the (common) case where + * only 1 discrete sampling frequency is used + */ +struct usb_audio_format_type1_descriptor_1freq { + struct usb_audio_format_type1_descriptor_head head; + struct usb_audio_format_discrete_sampling_frequency freqs[1]; +} __attribute__((packed)); + +#endif + +/**@}*/ diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb/cm3_usb_midi.h b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb/cm3_usb_midi.h new file mode 100644 index 000000000..8435c883e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb/cm3_usb_midi.h @@ -0,0 +1,190 @@ +/** @defgroup usb_audio_defines USB MIDI Type Definitions + +@brief Defined Constants and Types for the USB MIDI Type Definitions + +@ingroup USB_defines + +@version 1.0.0 + +@author @htmlonly © @endhtmlonly 2014 +Daniel Thompson + +@date 19 April 2014 + +LGPL License Terms @ref lgpl_license +*/ + +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 Daniel Thompson + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/**@{*/ + +#ifndef LIBOPENCM3_USB_MIDI_H +#define LIBOPENCM3_USB_MIDI_H + +#include + +/* + * Definitions from the USB_MIDI_ or usb_midi_ namespace come from: + * "Universal Serial Bus Class Definitions for MIDI Devices, Revision 1.0" + */ + +/* Appendix A.1: MS Class-Specific Interface Descriptor Subtypes */ +#define USB_MIDI_SUBTYPE_MS_DESCRIPTOR_UNDEFINED 0x00 +#define USB_MIDI_SUBTYPE_MS_HEADER 0x01 +#define USB_MIDI_SUBTYPE_MIDI_IN_JACK 0x02 +#define USB_MIDI_SUBTYPE_MIDI_OUT_JACK 0x03 +#define USB_MIDI_SUBTYPE_MIDI_ELEMENT 0x04 + +/* Appendix A.2: MS Class-Specific Endpoint Descriptor Subtypes */ +#define USB_MIDI_SUBTYPE_DESCRIPTOR_UNDEFINED 0x00 +#define USB_MIDI_SUBTYPE_MS_GENERAL 0x01 + +/* Appendix A.3: MS MIDI IN and OUT Jack types */ +#define USB_MIDI_JACK_TYPE_UNDEFINED 0x00 +#define USB_MIDI_JACK_TYPE_EMBEDDED 0x01 +#define USB_MIDI_JACK_TYPE_EXTERNAL 0x02 + +/* Appendix A.5.1 Endpoint Control Selectors */ +#define USB_MIDI_EP_CONTROL_UNDEFINED 0x00 +#define USB_MIDI_ASSOCIATION_CONTROL 0x01 + +/* Table 6-2: Class-Specific MS Interface Header Descriptor */ +struct usb_midi_header_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint16_t bcdMSC; + uint16_t wTotalLength; +} __attribute__((packed)); + +/* Table 6-3: MIDI IN Jack Descriptor */ +struct usb_midi_in_jack_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bJackType; + uint8_t bJackID; + uint8_t iJack; +} __attribute__((packed)); + +/* Table 6-4: MIDI OUT Jack Descriptor (head) */ +struct usb_midi_out_jack_descriptor_head { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bJackType; + uint8_t bJackID; + uint8_t bNrInputPins; + /* ... */ +} __attribute__((packed)); + +/* Table 6.4: MIDI OUT Jack Descriptor (body) */ +struct usb_midi_out_jack_descriptor_body { + /* ... */ + uint8_t baSourceID; + uint8_t baSourcePin; + /* ... */ +} __attribute__((packed)); + +/* Table 6.4: MIDI OUT Jack Descriptor (tail) */ +struct usb_midi_out_jack_descriptor_tail { + /* ... */ + uint8_t iJack; +} __attribute__((packed)); + +/* Table 6.4: MIDI OUT Jack Descriptor (single) + * + * This structure is a convenience covering the (normal) case where + * there is only one input pin. + */ +struct usb_midi_out_jack_descriptor { + struct usb_midi_out_jack_descriptor_head head; + struct usb_midi_out_jack_descriptor_body source[1]; + struct usb_midi_out_jack_descriptor_tail tail; +} __attribute__((packed)); + +/* Table 6-5: MIDI Element Descriptor (head) */ +struct usb_midi_element_descriptor_head { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bElementID; + uint8_t bNrInputPins; + /* ... */ +} __attribute__((packed)); + +/* Table 6-5: MIDI Element Descriptor (body) */ +struct usb_midi_element_descriptor_body { + /* ... */ + uint8_t baSourceID; + uint8_t baSourcePin; + /* ... */ +} __attribute__((packed)); + +/* Table 6-5: MIDI Element Descriptor (tail) */ +struct usb_midi_element_descriptor_tail { + /* ... */ + uint8_t bNrOutputPins; + uint8_t bInTerminalLink; + uint8_t bOutTerminalLink; + uint8_t bElCapsSize; + uint16_t bmElementCaps; /* host cannot assume this is 16-bit but device + can (since highest defined bitmap value in + v1.0 is bit 11) */ + uint8_t iElement; +} __attribute__((packed)); + +/* Table 6-5: MIDI Element Descriptor (single) + * + * This structure is a convenience covering the (common) case where + * there is only one input pin. + */ +struct usb_midi_element_descriptor { + struct usb_midi_element_descriptor_head head; + struct usb_midi_element_descriptor_body source[1]; + struct usb_midi_element_descriptor_tail tail; +} __attribute__((packed)); + +/* Table 6-7: Class-specific MS Bulk Data Endpoint Descriptor (head) */ +struct usb_midi_endpoint_descriptor_head { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bNumEmbMIDIJack; +} __attribute__((packed)); + +/* Table 6-7: Class-specific MS Bulk Data Endpoint Descriptor (body) */ +struct usb_midi_endpoint_descriptor_body { + uint8_t baAssocJackID; +} __attribute__((packed)); + +/* Table 6.7: Class-specific MS Bulk Data Endpoint Descriptor (single) + * + * This structure is a convenience covering the (normal) case where + * there is only one input pin. + */ +struct usb_midi_endpoint_descriptor { + struct usb_midi_endpoint_descriptor_head head; + struct usb_midi_endpoint_descriptor_body jack[1]; +} __attribute__((packed)); + +#endif + +/**@}*/ diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb/usb_midi_driver.c b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb/usb_midi_driver.c new file mode 100644 index 000000000..9abf77d12 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb/usb_midi_driver.c @@ -0,0 +1,428 @@ +#include +#include +#include +#include + +#include "usb_midi_driver.h" +#include "cm3_usb_audio.h" +#include "cm3_usb_midi.h" + +// Appendix B. "Example: Simple MIDI Adapter" from "Universal Serial Bus Device Class Definition for MIDI Devices", Revision 1.0 + +#define USB_VID 0x6666 +#define USB_PID 0x5119 + +#define USB_EP0_SIZE 8 + +#define USB_MIDI_EP_SIZE 64 +#define USB_MIDI_EP_IN 0x81 +#define USB_MIDI_EP_OUT 0x01 + +#define EP_CFG_DECONFIGURE 0 +#define EP_CFG_CONFIGURE 1 + +enum { + USB_STR_ZERO, + USB_STR_MANUFACTURER, + USB_STR_PRODUCT, + USB_STR_SERIAL_NUMBER, +}; + +/* + B.1 Device Descriptor +*/ +static const struct usb_device_descriptor device_descriptor = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 0, 0), // was 0x0110, 1.10 - current revision of USBspecification. + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = USB_SUBCLASS_NONE, + .bDeviceProtocol = USB_PROTO_NONE, + .bMaxPacketSize0 = USB_EP0_SIZE, + .idVendor = USB_VID, + .idProduct = USB_PID, + .bcdDevice = VERSION_BCD(1, 0, 0), + .iManufacturer = USB_STR_MANUFACTURER, + .iProduct = USB_STR_PRODUCT, + .iSerialNumber = USB_STR_SERIAL_NUMBER, + .bNumConfigurations = 1, +}; + +struct usb_audio_header_descriptor { + struct usb_audio_header_descriptor_head head; + struct usb_audio_header_descriptor_body body; +} __attribute__((packed)); + +struct usb_midi_jacks_descriptor { + struct usb_midi_header_descriptor header; + struct usb_midi_in_jack_descriptor in_embedded; + struct usb_midi_in_jack_descriptor in_external; + struct usb_midi_out_jack_descriptor out_embedded; + struct usb_midi_out_jack_descriptor out_external; +} __attribute__((packed)); + +struct MidiConfigDescriptor { + /* + B.2 Configuration Descriptor + */ + struct usb_config_descriptor config; + + /* + B.3 AudioControl Interface Descriptors + + The AudioControl interface describes the device structure (audio function topology) + and is used to manipulate the Audio Controls. This device has no audio function incorporated. + However, the AudioControl interface is mandatory and therefore both the standard AC interface + descriptor and the classspecific AC interface descriptor must be present. + The class-specific AC interface descriptor only contains the header descriptor. + */ + // B.3.1 Standard AC Interface Descriptor + struct usb_interface_descriptor audio_control_iface; + // B.3.2 Class-specific AC Interface Descriptor + struct usb_audio_header_descriptor audio_control_header; + + /* + B.4 MIDIStreaming Interface Descriptors + */ + // B.4.1 Standard MS Interface Descriptor + struct usb_interface_descriptor midi_streaming_iface; + // B.4.2 Class-specific MS Interface Descriptor + // B.4.3 MIDI IN Jack Descriptor + // B.4.4 MIDI OUT Jack Descriptor + struct usb_midi_jacks_descriptor midi_jacks; + + /* + B.5 Bulk OUT Endpoint Descriptors + */ + // B.5.1 Standard Bulk OUT Endpoint Descriptor + struct usb_endpoint_descriptor bulk_out; + // B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor + struct usb_midi_endpoint_descriptor midi_bulk_out; + + /* + B.6 Bulk IN Endpoint Descriptors + */ + // B.6.1 Standard Bulk IN Endpoint Descriptor + struct usb_endpoint_descriptor bulk_in; + // B.6.2 Class-specific MS Bulk IN Endpoint Descriptor + struct usb_midi_endpoint_descriptor midi_bulk_in; +} __attribute__((packed)); + +static const struct MidiConfigDescriptor config_descriptor = { + .config = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct MidiConfigDescriptor), + .bNumInterfaces = 2, /* control and data */ + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CFG_ATTR_RESERVED, + .bMaxPower = USB_CFG_POWER_MA(100), + }, + .audio_control_iface = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_AUDIO_SUBCLASS_CONTROL, + .bInterfaceProtocol = USB_PROTO_NONE, + .iInterface = 0, + }, + .audio_control_header = + { + .head = + { + .bLength = sizeof(struct usb_audio_header_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_AUDIO_TYPE_HEADER, + .bcdADC = VERSION_BCD(1, 0, 0), + .wTotalLength = sizeof(struct usb_audio_header_descriptor), + .bInCollection = 1, + }, + .body = + { + .baInterfaceNr = 1, + }, + }, + .midi_streaming_iface = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_AUDIO_SUBCLASS_MIDISTREAMING, + .bInterfaceProtocol = USB_PROTO_NONE, + .iInterface = 0, + }, + .midi_jacks = + { + .header = + { + .bLength = sizeof(struct usb_midi_header_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MIDI_SUBTYPE_MS_HEADER, + .bcdMSC = VERSION_BCD(1, 0, 0), + .wTotalLength = sizeof(struct usb_midi_jacks_descriptor), + }, + .in_embedded = + { + .bLength = sizeof(struct usb_midi_in_jack_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_IN_JACK, + .bJackType = USB_MIDI_JACK_TYPE_EMBEDDED, + .bJackID = 0x01, + .iJack = 0x00, + }, + .in_external = + { + .bLength = sizeof(struct usb_midi_in_jack_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_IN_JACK, + .bJackType = USB_MIDI_JACK_TYPE_EXTERNAL, + .bJackID = 0x02, + .iJack = 0x00, + }, + .out_embedded = + { + .head = + { + .bLength = sizeof(struct usb_midi_out_jack_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_OUT_JACK, + .bJackType = USB_MIDI_JACK_TYPE_EMBEDDED, + .bJackID = 0x03, + .bNrInputPins = 1, + }, + .source[0] = + { + .baSourceID = 0x02, + .baSourcePin = 0x01, + }, + .tail = + { + .iJack = 0x00, + }, + }, + .out_external = + { + .head = + { + .bLength = sizeof(struct usb_midi_out_jack_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MIDI_SUBTYPE_MIDI_OUT_JACK, + .bJackType = USB_MIDI_JACK_TYPE_EXTERNAL, + .bJackID = 0x04, + .bNrInputPins = 1, + }, + .source[0] = + { + .baSourceID = 0x01, + .baSourcePin = 0x01, + }, + .tail = + { + .iJack = 0x00, + }, + }, + }, + .bulk_out = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_MIDI_EP_OUT, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = USB_MIDI_EP_SIZE, + .bInterval = 0, + }, + .midi_bulk_out = + { + .head = + { + .bLength = sizeof(struct usb_midi_endpoint_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_ENDPOINT, + .bDescriptorSubType = USB_MIDI_SUBTYPE_MS_GENERAL, + .bNumEmbMIDIJack = 1, + }, + .jack[0] = + { + .baAssocJackID = 0x01, + }, + }, + .bulk_in = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_MIDI_EP_IN, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = USB_MIDI_EP_SIZE, + .bInterval = 0, + }, + .midi_bulk_in = + { + .head = + { + .bLength = sizeof(struct usb_midi_endpoint_descriptor), + .bDescriptorType = USB_AUDIO_DT_CS_ENDPOINT, + .bDescriptorSubType = USB_MIDI_SUBTYPE_MS_GENERAL, + .bNumEmbMIDIJack = 1, + }, + .jack[0] = + { + .baAssocJackID = 0x03, + }, + }, +}; + +static const struct usb_string_descriptor dev_manufacturer_string = + USB_STRING_DESC("Flipper Devices Inc."); + +static const struct usb_string_descriptor dev_product_string = + USB_STRING_DESC("Flipper MIDI Device"); + +static const struct usb_string_descriptor dev_serial_number_string = + USB_STRING_DESC("Serial Number"); + +static void midi_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx); +static void midi_deinit(usbd_device* dev); +static void midi_on_wakeup(usbd_device* dev); +static void midi_on_suspend(usbd_device* dev); +static usbd_respond midi_ep_config(usbd_device* dev, uint8_t cfg); +static usbd_respond midi_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); + +FuriHalUsbInterface midi_usb_interface = { + .init = midi_init, + .deinit = midi_deinit, + .wakeup = midi_on_wakeup, + .suspend = midi_on_suspend, + .dev_descr = (struct usb_device_descriptor*)&device_descriptor, + .cfg_descr = (void*)&config_descriptor, +}; + +typedef struct { + usbd_device* dev; + MidiRxCallback rx_callback; + void* context; + FuriSemaphore* semaphore_tx; + bool connected; +} MidiUsb; + +static MidiUsb midi_usb; + +void midi_usb_set_context(void* context) { + midi_usb.context = context; +} + +void midi_usb_set_rx_callback(MidiRxCallback callback) { + midi_usb.rx_callback = callback; +} + +size_t midi_usb_rx(uint8_t* buffer, size_t size) { + size_t len = usbd_ep_read(midi_usb.dev, USB_MIDI_EP_OUT, buffer, size); + return len; +} + +size_t midi_usb_tx(uint8_t* buffer, uint8_t size) { + if((midi_usb.semaphore_tx == NULL) || (midi_usb.connected == false)) return 0; + + furi_check(furi_semaphore_acquire(midi_usb.semaphore_tx, FuriWaitForever) == FuriStatusOk); + + if(midi_usb.connected) { + int32_t len = usbd_ep_write(midi_usb.dev, USB_MIDI_EP_IN, buffer, size); + return len; + } else { + return 0; + } +} + +static void midi_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); + UNUSED(ctx); + + midi_usb_interface.str_manuf_descr = (void*)&dev_manufacturer_string; + midi_usb_interface.str_prod_descr = (void*)&dev_product_string; + midi_usb_interface.str_serial_descr = (void*)&dev_serial_number_string; + midi_usb_interface.dev_descr->idVendor = USB_VID; + midi_usb_interface.dev_descr->idProduct = USB_PID; + + midi_usb.dev = dev; + if(midi_usb.semaphore_tx == NULL) midi_usb.semaphore_tx = furi_semaphore_alloc(1, 1); + + usbd_reg_config(dev, midi_ep_config); + usbd_reg_control(dev, midi_control); + + usbd_connect(dev, true); +} + +static void midi_deinit(usbd_device* dev) { + midi_usb.connected = false; + midi_usb.dev = NULL; + furi_semaphore_free(midi_usb.semaphore_tx); + + usbd_reg_config(dev, NULL); + usbd_reg_control(dev, NULL); +} + +static void midi_on_wakeup(usbd_device* dev) { + UNUSED(dev); + if(!midi_usb.connected) { + midi_usb.connected = true; + } +} + +static void midi_on_suspend(usbd_device* dev) { + UNUSED(dev); + if(midi_usb.connected) { + midi_usb.connected = false; + } +} + +static void midi_tx_rx(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(ep); + + switch(event) { + case usbd_evt_eptx: + furi_semaphore_release(midi_usb.semaphore_tx); + break; + case usbd_evt_eprx: + if(midi_usb.rx_callback != NULL) { + midi_usb.rx_callback(midi_usb.context); + } + break; + default: + break; + } +} + +static usbd_respond midi_ep_config(usbd_device* dev, uint8_t cfg) { + switch(cfg) { + case EP_CFG_DECONFIGURE: + usbd_ep_deconfig(dev, USB_MIDI_EP_OUT); + usbd_ep_deconfig(dev, USB_MIDI_EP_IN); + usbd_reg_endpoint(dev, USB_MIDI_EP_OUT, NULL); + usbd_reg_endpoint(dev, USB_MIDI_EP_IN, NULL); + return usbd_ack; + case EP_CFG_CONFIGURE: + usbd_ep_config(dev, USB_MIDI_EP_OUT, USB_EPTYPE_BULK, USB_MIDI_EP_SIZE); + usbd_ep_config(dev, USB_MIDI_EP_IN, USB_EPTYPE_BULK, USB_MIDI_EP_SIZE); + usbd_reg_endpoint(dev, USB_MIDI_EP_OUT, midi_tx_rx); + usbd_reg_endpoint(dev, USB_MIDI_EP_IN, midi_tx_rx); + return usbd_ack; + default: + return usbd_fail; + } +} + +static usbd_respond midi_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(dev); + UNUSED(req); + UNUSED(callback); + + return usbd_fail; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb/usb_midi_driver.h b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb/usb_midi_driver.h new file mode 100644 index 000000000..d385efcb5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb/usb_midi_driver.h @@ -0,0 +1,14 @@ +#pragma once +#include + +extern FuriHalUsbInterface midi_usb_interface; + +typedef void (*MidiRxCallback)(void* context); + +void midi_usb_set_context(void* context); + +void midi_usb_set_rx_callback(MidiRxCallback callback); + +size_t midi_usb_rx(uint8_t* buffer, size_t size); + +size_t midi_usb_tx(uint8_t* buffer, uint8_t size); \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb_midi.c b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb_midi.c new file mode 100644 index 000000000..ec8856301 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb_midi.c @@ -0,0 +1,86 @@ +#include +#include +#include "usb/usb_midi_driver.h" +#include "midi/parser.h" +#include "midi/usb_message.h" +#include + +float note_to_frequency(int note) { + float a = 440; + return (a / 32) * powf(2, ((note - 9) / 12.0)); +} + +typedef enum { + MidiThreadEventStop = (1 << 0), + MidiThreadEventRx = (1 << 1), + MidiThreadEventAll = MidiThreadEventStop | MidiThreadEventRx, +} MidiThreadEvent; + +static void midi_rx_callback(void* context) { + furi_assert(context); + FuriThreadId thread_id = (FuriThreadId)context; + furi_thread_flags_set(thread_id, MidiThreadEventRx); +} + +int32_t usb_midi_app(void* p) { + UNUSED(p); + + FuriHalUsbInterface* usb_config_prev; + usb_config_prev = furi_hal_usb_get_config(); + midi_usb_set_context(furi_thread_get_id(furi_thread_get_current())); + midi_usb_set_rx_callback(midi_rx_callback); + furi_hal_usb_set_config(&midi_usb_interface, NULL); + + MidiParser* parser = midi_parser_alloc(); + uint32_t events; + uint8_t current_note = 255; + + while(1) { + events = furi_thread_flags_wait(MidiThreadEventAll, FuriFlagWaitAny, FuriWaitForever); + + if(!(events & FuriFlagError)) { + if(events & MidiThreadEventRx) { + uint8_t buffer[64]; + size_t size = midi_usb_rx(buffer, sizeof(buffer)); + // loopback + // midi_usb_tx(buffer, size); + size_t start = 0; + while(start < size) { + CodeIndex code_index = code_index_from_data(buffer[start]); + uint8_t data_size = usb_message_data_size(code_index); + if(data_size == 0) break; + + start += 1; + for(size_t j = 0; j < data_size; j++) { + if(midi_parser_parse(parser, buffer[start + j])) { + MidiEvent* event = midi_parser_get_message(parser); + if(event->type == NoteOn) { + NoteOnEvent note_on = AsNoteOn(event); + current_note = note_on.note; + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + furi_hal_speaker_start( + note_to_frequency(note_on.note), + note_on.velocity / 127.0f); + } + } else if(event->type == NoteOff) { + NoteOffEvent note_off = AsNoteOff(event); + if(note_off.note == current_note) { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } + } + } + } + } + start += data_size; + } + } + } + } + + midi_parser_free(parser); + furi_hal_usb_set_config(usb_config_prev, NULL); + + return 0; +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb_midi.png b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb_midi.png new file mode 100644 index 000000000..6d0ac6fed Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/usb_midi/usb_midi.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/videopoker/application.fam b/Applications/Official/DEV_FW/source/xMasterX/videopoker/application.fam new file mode 100644 index 000000000..c7b46047b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/videopoker/application.fam @@ -0,0 +1,12 @@ +App( + appid="VideoPoker", + name="Video Poker", + apptype=FlipperAppType.EXTERNAL, + entry_point="video_poker_app", + cdefines=["APP_VIDEOPOKER_GAME"], + requires=["gui"], + stack_size=2 * 1024, + order=270, + fap_icon="pokerIcon.png", + fap_category="Games_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/videopoker/poker.c b/Applications/Official/DEV_FW/source/xMasterX/videopoker/poker.c new file mode 100644 index 000000000..0205d1319 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/videopoker/poker.c @@ -0,0 +1,820 @@ +#include +#include +#include +#include +#include +#include +#include +#include "assets_icons.h" +#include + +/* Core game logic from +https://github.com/Yaoir/VideoPoker-C +*/ + +/* KNOWN BUGS +This has been converted from a standalone PC console app to flipper +All of the original input/output handing code has been ripped out +Original code also used TONS of defines and everything was a global. +As is, it does what I wanted and doesn't seem to have major issues, so that's pretty good. +Game logic is handled during input and this is a bit of a mess of nested ifs. +Sometimes duplicate cards will show up. there is a function to test this. I should use it better. + +*/ + +#define TAG "Video Poker" + +static void Shake(void) { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_single_vibro); + furi_record_close(RECORD_NOTIFICATION); +} + +typedef struct { + int index; /* cards value, minus 1 */ + char* sym; /* text appearance */ + int suit; /* card's suit (see just below) */ + int gone; /* true if it's been dealt */ + int held; /* for hand */ +} PokerPlayer_card; + +typedef struct { + FuriMutex** model_mutex; + FuriMessageQueue* event_queue; + ViewPort* view_port; + Gui* gui; + PokerPlayer_card hand[5]; + PokerPlayer_card shand[5]; + PokerPlayer_card deck[52]; + int GameType; /* What rules are we using */ + int held[5]; + int score; + int highscore; + int pot; + int GameState; + int selected; + int bet; + int minbet; +} PokerPlayer; + +/* GameState +0=Splash/help, OK button (later on up/down for rules or settings) +1=cards down, betting enabled, left/right to change bet, OK to confirm +2=first hand, holding enabled, left/right to pick card, OK to hold/unhold card, down to confirm +3=second hand, only confirm to claim rewards +4=game over/won +5=WIP saving gamestate +*/ + +/* +#define AllAmerican 0 +#define TensOrBetter 1 +#define BonusPoker 2 +#define DoubleBonus 3 +#define DoubleBonusBonus 4 +#define JacksOrBetter 5 +#define JacksOrBetter95 6 +#define JacksOrBetter86 7 +#define JacksOrBetter85 8 +#define JacksOrBetter75 9 +#define JacksOrBetter65 10 +#define NUMGAMES 11 +*/ +/* + The game in play. Default is Jacks or Better, + which is coded into initialization of static data +*/ +const char* gamenames[11] = { + "All American", + "Tens or Better", + "Bonus Poker", + "Double Bonus", + "Double Bonus Bonus", + "Jacks or Better", + "9/5 Jacks or Better", + "8/6 Jacks or Better", + "8/5 Jacks or Better", + "7/5 Jacks or Better", + "6/5 Jacks or Better"}; + +PokerPlayer_card deck[52] = { + /* index, card name, suit, gone */ + /* Clubs:0 Diamonds:1 Hearts: 2 Spades: 3 */ + {1, "2", 0, 0, 0}, {2, "3", 0, 0, 0}, {3, "4", 0, 0, 0}, {4, "5", 0, 0, 0}, + {5, "6", 0, 0, 0}, {6, "7", 0, 0, 0}, {7, "8", 0, 0, 0}, {8, "9", 0, 0, 0}, + {9, "10", 0, 0, 0}, {10, "J", 0, 0, 0}, {11, "Q", 0, 0, 0}, {12, "K", 0, 0, 0}, + {13, "A", 0, 0, 0}, + + {1, "2", 1, 0, 0}, {2, "3", 1, 0, 0}, {3, "4", 1, 0, 0}, {4, "5", 1, 0, 0}, + {5, "6", 1, 0, 0}, {6, "7", 1, 0, 0}, {7, "8", 1, 0, 0}, {8, "9", 1, 0, 0}, + {9, "10", 1, 0, 0}, {10, "J", 1, 0, 0}, {11, "Q", 1, 0, 0}, {12, "K", 1, 0, 0}, + {13, "A", 1, 0, 0}, + + {1, "2", 2, 0, 0}, {2, "3", 2, 0, 0}, {3, "4", 2, 0, 0}, {4, "5", 2, 0, 0}, + {5, "6", 2, 0, 0}, {6, "7", 2, 0, 0}, {7, "8", 2, 0, 0}, {8, "9", 2, 0, 0}, + {9, "10", 2, 0, 0}, {10, "J", 2, 0, 0}, {11, "Q", 2, 0, 0}, {12, "K", 2, 0, 0}, + {13, "A", 2, 0, 0}, + + {1, "2", 3, 0, 0}, {2, "3", 3, 0, 0}, {3, "4", 3, 0, 0}, {4, "5", 3, 0, 0}, + {5, "6", 3, 0, 0}, {6, "7", 3, 0, 0}, {7, "8", 3, 0, 0}, {8, "9", 3, 0, 0}, + {9, "10", 3, 0, 0}, {10, "J", 3, 0, 0}, {11, "Q", 3, 0, 0}, {12, "K", 3, 0, 0}, + {13, "A", 3, 0, 0}, +}; + +/* +Image Format +0x01 = Compressed +0x00 = Reserved Section +0xa4,0x01 = 0x1a4, or, 420 - the size of the compressed array, minus this header. +Rest of the data is char array output from heatshrink of the original XBM char array. +Calculated Header: 0x01,0x00,0xa4,0x01 +from furi_hal_compress.c: +typedef struct { + uint8_t is_compressed; + uint8_t reserved; + uint16_t compressed_buff_size; +} FuriHalCompressHeader; +*/ + +const uint8_t _I_Splash_128x64_0[] = { + 0x01, 0x00, 0x8a, 0x02, 0x00, 0x78, 0x02, 0x60, 0xe0, 0x54, 0xc0, 0x03, 0x9f, 0xc0, 0x0f, 0x5a, + 0x04, 0x04, 0x1e, 0xdf, 0x08, 0x78, 0x0c, 0x60, 0xc0, 0x21, 0x90, 0x40, 0xa3, 0x00, 0xf5, 0xfe, + 0x61, 0xc1, 0xe9, 0x1e, 0x8e, 0x59, 0xf0, 0x02, 0x24, 0x9f, 0x70, 0xc0, 0x63, 0x03, 0x01, 0x0c, + 0x0b, 0xc1, 0x80, 0xbc, 0x83, 0xd3, 0x3f, 0x63, 0x98, 0x03, 0xcf, 0x88, 0x02, 0x1c, 0x31, 0x5d, + 0x38, 0xf6, 0x19, 0xc0, 0xa0, 0xfc, 0x93, 0x13, 0x12, 0xf0, 0x38, 0x76, 0x08, 0xc7, 0x00, 0x1e, + 0x5e, 0x8b, 0xcc, 0x32, 0x86, 0x0f, 0x4f, 0x0c, 0x80, 0x06, 0x20, 0x72, 0xe4, 0x5e, 0x33, 0xd4, + 0x73, 0xf2, 0x5d, 0xe2, 0x10, 0xef, 0xe6, 0x02, 0x0f, 0x07, 0x84, 0x4c, 0x33, 0xd2, 0x70, 0x79, + 0xd8, 0x2e, 0x11, 0x88, 0x3d, 0xff, 0xc1, 0xc7, 0x83, 0xc4, 0x20, 0x10, 0xc9, 0x18, 0x3d, 0x27, + 0x18, 0x8c, 0x3c, 0xde, 0xe1, 0xe6, 0x87, 0x7e, 0x0c, 0x62, 0x12, 0x10, 0x01, 0xce, 0x31, 0x9c, + 0x39, 0x9c, 0x62, 0x67, 0x0f, 0x83, 0x7f, 0x27, 0xe0, 0xf5, 0x8c, 0x71, 0xbc, 0x31, 0x8c, 0xc4, + 0xe2, 0x1e, 0x62, 0x1e, 0x02, 0xe0, 0x80, 0x05, 0x1c, 0xe1, 0xdc, 0x23, 0x97, 0xc8, 0xe4, 0x5c, + 0x12, 0x50, 0x40, 0x7a, 0x43, 0x38, 0x77, 0x88, 0xf4, 0x36, 0x3d, 0x1f, 0x04, 0x94, 0x20, 0x1e, + 0x98, 0xce, 0x0d, 0xbe, 0x37, 0x0d, 0xcd, 0xbd, 0x0c, 0x7e, 0xbe, 0xce, 0x07, 0x1f, 0xf3, 0xfc, + 0xf8, 0xb2, 0x8d, 0x30, 0x20, 0x53, 0xbe, 0x60, 0x06, 0x03, 0x78, 0xf0, 0x06, 0x4c, 0x1e, 0x34, + 0x10, 0x29, 0x5e, 0x05, 0x0f, 0x00, 0xa0, 0x40, 0x24, 0x20, 0x52, 0x76, 0x88, 0x01, 0xc1, 0xe3, + 0x11, 0x05, 0xc3, 0xe9, 0x20, 0x10, 0x97, 0x01, 0xcf, 0xc1, 0xf2, 0x81, 0x3f, 0xe7, 0xfc, 0x66, + 0xf4, 0x02, 0xf1, 0xc0, 0x3f, 0xdf, 0xf0, 0x30, 0xc6, 0x1e, 0xe5, 0xff, 0x81, 0xf0, 0x3f, 0xe5, + 0xb2, 0x80, 0x7f, 0xc1, 0xe5, 0x1c, 0x03, 0x0f, 0xe3, 0xff, 0x1f, 0xf8, 0x02, 0x48, 0x00, 0x31, + 0xfe, 0x0b, 0xa4, 0x61, 0xcc, 0x62, 0xfc, 0x4f, 0xe3, 0x0f, 0x31, 0x41, 0x0e, 0x02, 0x07, 0x01, + 0x07, 0x8a, 0xb4, 0xa3, 0x84, 0x71, 0x8f, 0xff, 0x20, 0x77, 0x00, 0x78, 0x95, 0x46, 0x06, 0x13, + 0x10, 0x78, 0xef, 0x3f, 0x5f, 0xfc, 0xff, 0xea, 0x07, 0xf0, 0x37, 0x90, 0x3c, 0x78, 0x00, 0xf2, + 0xae, 0x7f, 0x77, 0xf7, 0xaf, 0xec, 0x0f, 0x88, 0x41, 0x1b, 0x06, 0x02, 0x03, 0xc0, 0x02, 0x8c, + 0x08, 0x5c, 0x37, 0xff, 0xa9, 0x3c, 0x7b, 0xcc, 0x52, 0xe0, 0x70, 0x7c, 0x31, 0x89, 0xe4, 0xff, + 0xfb, 0xff, 0xdf, 0x8c, 0x46, 0x03, 0x1f, 0x34, 0x17, 0x83, 0xe1, 0x71, 0x8f, 0x6f, 0xe7, 0xe0, + 0xc1, 0x8f, 0xfd, 0x20, 0x18, 0x65, 0x59, 0x47, 0xaf, 0x9b, 0x8b, 0x9e, 0x6f, 0xe7, 0x1f, 0x16, + 0x0c, 0x3e, 0x3d, 0x00, 0xe4, 0x43, 0xd1, 0xe5, 0x3f, 0xe6, 0x6e, 0xfb, 0x39, 0x88, 0x67, 0xea, + 0xff, 0xc5, 0x22, 0x8f, 0xc0, 0xf0, 0x41, 0x71, 0xe7, 0x76, 0xf9, 0x98, 0x48, 0x64, 0x17, 0x59, + 0x38, 0x05, 0x8f, 0xc0, 0xd0, 0x5f, 0xe8, 0x0f, 0x1a, 0xdb, 0xe6, 0xb1, 0xd1, 0xa0, 0x50, 0x85, + 0x59, 0x7e, 0x16, 0x05, 0x06, 0x80, 0x71, 0xbf, 0xf7, 0x19, 0x85, 0x99, 0x74, 0x6d, 0x31, 0x02, + 0x10, 0x88, 0x7c, 0xdd, 0xdb, 0x84, 0x62, 0x7c, 0x0f, 0x38, 0xe5, 0xf0, 0x1e, 0x97, 0xce, 0x67, + 0xbc, 0xb6, 0x40, 0xa3, 0x98, 0x00, 0xc5, 0x76, 0x53, 0x8c, 0x67, 0x1e, 0x07, 0x0e, 0x63, 0x0a, + 0xe4, 0x9c, 0x62, 0x0f, 0x11, 0x41, 0x95, 0x88, 0x1e, 0x41, 0xd1, 0x8c, 0x49, 0x80, 0xe6, 0x00, + 0x50, 0xb8, 0xa3, 0x07, 0xf1, 0x7f, 0x06, 0xb8, 0x00, 0x61, 0xce, 0xb2, 0x9c, 0x53, 0x01, 0xf3, + 0xf0, 0x55, 0x97, 0xd0, 0x3f, 0x40, 0x03, 0xfd, 0x33, 0xc8, 0x01, 0x71, 0x92, 0x78, 0x80, 0x2f, + 0x80, 0x6f, 0x20, 0x03, 0xff, 0x23, 0xe7, 0x02, 0x02, 0x18, 0x01, 0xa3, 0x91, 0x00, 0x18, 0xc3, + 0x20, 0x91, 0xc0, 0x7c, 0x7f, 0x83, 0x42, 0xaa, 0x1f, 0xe0, 0xbe, 0x60, 0x46, 0xa2, 0x81, 0xe2, + 0x24, 0x21, 0xf9, 0x54, 0x14, 0x18, 0x9e, 0x3f, 0xe4, 0x29, 0x00, 0x12, 0x0e, 0xb0, 0x28, 0x50, + 0x3c, 0x60, 0x50, 0x85, 0xf4, 0x7f, 0xb8, 0x3f, 0xf3, 0xf8, 0x83, 0xe0, 0x00, 0x38, 0x6e, 0x0c, + 0xc3, 0xf2, 0x2f, 0x94, 0x09, 0x07, 0xc7, 0xf7, 0x3f, 0xfe, 0x0d, 0xc4, 0x00, 0xfc, 0x4c, 0x05, + 0x86, 0x15, 0x23, 0x92, 0x03, 0xe7, 0xf9, 0x80, 0x0f, 0x97, 0x52, 0x0c, 0x2f, 0xb1, 0xf8, 0xe3, + 0x01, 0xf3, 0x82, 0x27, 0x8d, 0xe6, 0x41, 0x1c, 0x17, 0xcf, 0xfc, 0x3e, 0x64, 0xf8, +}; +const uint8_t* _I_Splash_128x64[] = {_I_Splash_128x64_0}; +const Icon I_Splash_128x64 = + {.width = 128, .height = 64, .frame_count = 1, .frame_rate = 0, .frames = _I_Splash_128x64}; + +/* +const uint8_t _I_BadEnd_128x64_0[] = { + 0x01, 0x00, 0xDF, 0x01, 0x00, 0x2c, 0x12, 0x01, 0x02, 0x80, 0x40, 0x70, 0x10, 0x0a, 0x04, 0x02, + 0x41, 0x3e, 0xcf, 0x63, 0xfb, 0xfe, 0xc8, 0x18, 0x3e, 0x6f, 0xdb, 0xfc, 0xf8, 0x3c, 0x60, 0xe0, + 0xf9, 0xb3, 0x6c, 0xf3, 0x3c, 0x1b, 0x6c, 0x18, 0x5f, 0x40, 0xf1, 0xe7, 0xdb, 0xc1, 0xf4, 0x2f, + 0x10, 0x78, 0xdb, 0xbc, 0xdf, 0xf0, 0x04, 0x59, 0x81, 0xe3, 0xc1, 0xb6, 0x41, 0x83, 0xd1, 0x00, + 0xbf, 0x6c, 0xc9, 0xe6, 0x0f, 0x91, 0xf8, 0x9b, 0xcc, 0x1f, 0x20, 0x06, 0x07, 0xf8, 0x3e, 0x0b, + 0x32, 0x00, 0x50, 0x88, 0xc4, 0x20, 0x10, 0x85, 0xfd, 0x03, 0xfc, 0x1f, 0xe0, 0xff, 0x07, 0xf9, + 0x7f, 0xc3, 0xdc, 0x89, 0x10, 0x7d, 0x00, 0x04, 0x1f, 0xe0, 0xfd, 0xfc, 0x40, 0xc1, 0xfb, 0x07, + 0x8e, 0x2f, 0xf3, 0x9f, 0x00, 0xb0, 0x7f, 0x97, 0xf6, 0x0a, 0x11, 0x10, 0xa3, 0xec, 0x10, 0x21, + 0x32, 0x07, 0xd0, 0x18, 0x40, 0xa2, 0x0f, 0xb0, 0x20, 0x81, 0xc4, 0x1f, 0xeb, 0xfa, 0xbf, 0x84, + 0x86, 0x01, 0xc8, 0x5f, 0xd0, 0x0c, 0x81, 0xe2, 0x05, 0x10, 0x7e, 0xdc, 0xc1, 0xf5, 0x01, 0xe0, + 0x41, 0xf2, 0x17, 0xf0, 0x7d, 0xaf, 0x0a, 0x7e, 0x0f, 0xbf, 0x84, 0x7f, 0x21, 0x1f, 0x2b, 0x8e, + 0x3c, 0xbe, 0xd3, 0xf0, 0x78, 0xc4, 0xfa, 0x0b, 0xf2, 0x00, 0x08, 0x81, 0xa1, 0xf3, 0x08, 0x9f, + 0xc0, 0x1e, 0x57, 0x00, 0x7b, 0x60, 0x60, 0x3e, 0x08, 0x4f, 0x80, 0x1e, 0x59, 0x05, 0xc1, 0x03, + 0xce, 0xc3, 0x00, 0x2f, 0x88, 0x3c, 0xe2, 0x10, 0x20, 0x78, 0xbd, 0xc6, 0xff, 0x7c, 0x8c, 0x0e, + 0x48, 0x1e, 0x90, 0x48, 0x47, 0xe2, 0x06, 0x1b, 0x1e, 0x3c, 0x1c, 0x1e, 0x80, 0x01, 0x93, 0xad, + 0x06, 0x1e, 0x0a, 0x28, 0x04, 0x18, 0x1e, 0x81, 0xe1, 0x90, 0x20, 0x46, 0x49, 0xa9, 0x91, 0x3e, + 0x46, 0xf8, 0x0f, 0xac, 0x48, 0x3c, 0xb0, 0x82, 0x52, 0x07, 0xa1, 0x08, 0x43, 0xe5, 0x72, 0x93, + 0x41, 0x7e, 0x01, 0x01, 0x07, 0xc7, 0x8a, 0x97, 0xa9, 0x39, 0x88, 0xa0, 0x7f, 0x00, 0xf2, 0x08, + 0x0c, 0x03, 0x25, 0x54, 0x88, 0xe9, 0x66, 0x11, 0xc2, 0x99, 0x9e, 0x07, 0xff, 0x13, 0x90, 0x7f, + 0xb2, 0x60, 0xf2, 0xaa, 0x79, 0x1b, 0xe5, 0x01, 0xfe, 0x1f, 0xca, 0x41, 0x08, 0xb0, 0xd4, 0xe2, + 0x33, 0x9c, 0x9f, 0x13, 0xff, 0x07, 0xc0, 0x0c, 0x04, 0x1e, 0x54, 0x08, 0x40, 0x64, 0x80, 0x03, + 0x84, 0xff, 0xc0, 0x68, 0x10, 0x0f, 0x80, 0x3d, 0x13, 0xc2, 0x00, 0x28, 0x25, 0xfa, 0x00, 0x0f, + 0x76, 0x60, 0x83, 0xcc, 0x04, 0x20, 0xc1, 0x07, 0xaf, 0xc8, 0x52, 0x52, 0x00, 0x7a, 0x2f, 0xcc, + 0x16, 0x31, 0x30, 0x49, 0x48, 0x17, 0xe5, 0x20, 0xc0, 0x23, 0xce, 0x81, 0x80, 0x88, 0xe6, 0x24, + 0x7c, 0x69, 0xc0, 0xd0, 0xa2, 0x1c, 0x00, 0x79, 0x85, 0x07, 0xe3, 0xa4, 0xb0, 0x4a, 0x64, 0xa0, + 0xf3, 0x57, 0x9d, 0x82, 0x01, 0x80, 0x84, 0x54, 0xb2, 0x19, 0x48, 0x91, 0x90, 0xa2, 0x1f, 0x00, + 0x79, 0x0f, 0x87, 0x80, 0x0f, 0x44, 0x21, 0x03, 0xd0, 0x3e, 0x26, 0x01, 0xa6, 0x44, 0x2c, 0x79, + 0xc0, 0x79, 0xb3, 0xc4, 0xbe, 0x5e, 0x01, 0x08, 0x80, 0x09, 0x56, 0x20, 0x01, 0x98, 0x03, 0xc4, + 0xfe, 0x51, 0x0b, 0xf8, 0x3c, 0xf8, 0x00, 0x32, 0x9c, 0x7f, 0x01, 0xe8, 0x1f, 0x40, 0x21, 0xd7, + 0x81, 0xfb, 0x80, 0xcf, 0x8f, 0x44, 0x1e, 0x7c, 0x88, 0x38, 0x28, 0x70, 0xe4, 0x92, 0xff, 0xc7, + 0xef, 0x1f, 0x80, +}; +const uint8_t* _I_BadEnd_128x64[] = {_I_BadEnd_128x64_0}; +const Icon I_BadEnd_128x64 = + {.width = 128, .height = 64, .frame_count = 1, .frame_rate = 0, .frames = _I_BadEnd_128x64}; +*/ /* space savings until external apps are possible */ +const uint8_t _I_Hand_12x10_0[] = { + 0x01, 0x00, 0x11, 0x00, 0x8c, 0x40, 0x25, 0x00, 0x16, 0xb4, 0x40, + 0x35, 0x10, 0x1d, 0x5c, 0x1b, 0x5b, 0x0a, 0x84, 0xc2, 0x80, +}; +const uint8_t* _I_Hand_12x10[] = {_I_Hand_12x10_0}; +const Icon I_Hand_12x10 = + {.width = 12, .height = 10, .frame_count = 1, .frame_rate = 0, .frames = _I_Hand_12x10}; + +const uint8_t _I_CardBack_22x35_0[] = { + 0x01, 0x00, 0x23, 0x00, 0xfe, 0x7f, 0xe1, 0xf0, 0x28, 0x04, 0x43, 0xe3, 0xff, + 0x91, 0xea, 0x75, 0x52, 0x6a, 0xad, 0x56, 0x5b, 0xad, 0xd5, 0x4a, 0x80, 0xbe, + 0x05, 0xf0, 0x2f, 0x81, 0x7c, 0x0b, 0x45, 0x32, 0x2c, 0x91, 0x7c, 0x8c, 0xa4, +}; +const uint8_t* _I_CardBack_22x35[] = {_I_CardBack_22x35_0}; +const Icon I_CardBack_22x35 = + {.width = 22, .height = 35, .frame_count = 1, .frame_rate = 0, .frames = _I_CardBack_22x35}; + +//uncompressed but lol +const uint8_t _I_club_7x8_0[] = {0x00, 0x08, 0x1c, 0x1c, 0x6b, 0x7f, 0x36, 0x08, 0x1c}; +const uint8_t* _I_club_7x8[] = {_I_club_7x8_0}; +const Icon I_club_7x8 = + {.width = 7, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_club_7x8}; + +//uncompressed but lol +const uint8_t _I_diamond_7x8_0[] = {0x00, 0x00, 0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08}; +const uint8_t* _I_diamond_7x8[] = {_I_diamond_7x8_0}; +const Icon I_diamond_7x8 = + {.width = 7, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_diamond_7x8}; + +//uncompressed +const uint8_t _I_hearts_7x8_0[] = {0x00, 0x00, 0x36, 0x7f, 0x7f, 0x7f, 0x3e, 0x1c, 0x08}; +const uint8_t* _I_hearts_7x8[] = {_I_hearts_7x8_0}; +const Icon I_hearts_7x8 = + {.width = 7, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_hearts_7x8}; + +//uncompressed +const uint8_t _I_spade_7x8_0[] = {0x00, 0x08, 0x1c, 0x3e, 0x7f, 0x7f, 0x36, 0x08, 0x1c}; +const uint8_t* _I_spade_7x8[] = {_I_spade_7x8_0}; +const Icon I_spade_7x8 = + {.width = 7, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_spade_7x8}; + +// They only included Numeric Profont22 glyphs and I don't want to fuck up the font embeds right now sooo.. + +const uint8_t _I_King_7x8_0[] = { + 0x01, 0x00, 0x1a, 0x00, 0xc1, 0xc0, 0xf8, 0x70, 0x1f, 0x1c, 0x02, 0xe7, 0x00, 0x9d, 0xc0, + 0x23, 0xf0, 0x08, 0x78, 0x0c, 0x80, 0xe2, 0x0b, 0x10, 0x78, 0x84, 0xc4, 0x2e, 0x20, 0x01, +}; +const uint8_t* _I_King_7x8[] = {_I_King_7x8_0}; +const Icon I_King_7x8 = + {.width = 10, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_King_7x8}; + +const uint8_t _I_Queen_7x8_0[] = { + 0x01, 0x00, 0x13, 0x00, 0xfe, 0x40, 0x3f, 0xd0, 0x1c, 0x3c, 0x0c, 0x01, + 0x76, 0x38, 0x1f, 0x8e, 0x07, 0xc7, 0x81, 0x85, 0x47, 0xf9, 0x01, +}; +const uint8_t* _I_Queen_7x8[] = {_I_Queen_7x8_0}; +const Icon I_Queen_7x8 = + {.width = 10, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_Queen_7x8}; + +const uint8_t _I_Jack_7x8_0[] = { + 0x01, + 0x00, + 0x0D, + 0x00, + 0x80, + 0x40, + 0xc0, + 0x3a, + 0x00, + 0x5c, + 0x3c, + 0x0f, + 0xfd, + 0x01, + 0xfe, + 0x40, + 0x00, +}; +const uint8_t* _I_Jack_7x8[] = {_I_Jack_7x8_0}; +const Icon I_Jack_7x8 = + {.width = 10, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_Jack_7x8}; + +const uint8_t _I_Ace_7x8_0[] = { + 0x01, 0x00, 0x13, 0x00, 0x98, 0x40, 0x2f, 0x00, 0x12, 0xe6, 0x00, 0x4b, + 0x0d, 0x01, 0x00, 0x8c, 0x0e, 0x07, 0xff, 0x00, 0x90, 0x01, 0xc0, +}; +const uint8_t* _I_Ace_7x8[] = {_I_Ace_7x8_0}; +const Icon I_Ace_7x8 = + {.width = 10, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_Ace_7x8}; + +const uint8_t _I_Ten_7x8_0[] = { + 0x01, 0x00, 0x29, 0x00, 0x86, 0x7f, 0x00, 0x43, 0xfe, 0x80, 0xc3, 0xf0, 0xf0, 0x38, 0x7e, + 0x0e, 0x07, 0x0c, 0xe1, 0x80, 0x87, 0xc6, 0x02, 0x1b, 0x98, 0x08, 0x67, 0x60, 0x21, 0x8f, + 0x80, 0x86, 0x1e, 0x02, 0x18, 0x38, 0x08, 0x43, 0x43, 0x7f, 0x10, 0x0d, 0xfc, 0x4c, 0x20, +}; +const uint8_t* _I_Ten_7x8[] = {_I_Ten_7x8_0}; +const Icon I_Ten_7x8 = + {.width = 18, .height = 14, .frame_count = 1, .frame_rate = 0, .frames = _I_Ten_7x8}; + +const Icon card_suit[4] = {I_diamond_7x8, I_club_7x8, I_hearts_7x8, I_spade_7x8}; + +const Icon card_face[5] = {I_Ten_7x8, I_Jack_7x8, I_Queen_7x8, I_King_7x8, I_Ace_7x8}; + +/* Sanity check: check that there are no duplicate cards in hand */ + +static void playcard(PokerPlayer* app) { + int i, c, crd; + + int hold[5]; + hold[5] = 2; + // int digit; + c = 1; + c++; + c = hold[5]; /* FIX for unused-but-set-variable */ + /* initialize deck */ + for(i = 0; i < 52; i++) deck[i].gone = 0; + + /* initialize hold[] */ + for(i = 0; i < 5; i++) hold[i] = 1; + + /* app->score -= bet; */ + if(app->score > app->highscore) { + app->highscore = app->score; + } /* record high water mark */ + + for(i = 0; i < 5; i++) { + /* find a card not already dealt */ + do crd = random() % 52; + while(deck[crd].gone); + hold[i] = 1; + deck[crd].gone = 1; + if(!app->held[i]) { + app->hand[i] = deck[crd]; + } + } +} + +static int check_for_dupes(PokerPlayer* app) { + int i, j; + + for(i = 0; i < 5; i++) { + for(j = i + 1; j < 5; j++) { + if(app->hand[i].index == app->hand[j].index && app->hand[i].suit == app->hand[j].suit) + return 0; + } + } + + return 1; +} + +/* Functions that recognize winning hands */ + +/* + Flush: + returns 1 if the sorted hand is a flush +*/ + +static int flush(PokerPlayer* app) { + if(app->shand[0].suit == app->shand[1].suit && app->shand[1].suit == app->shand[2].suit && + app->shand[2].suit == app->shand[3].suit && app->shand[3].suit == app->shand[4].suit) + return 1; + + return 0; +} + +/* + Straight: + returns 1 if the sorted hand is a straight +*/ + +static int straight(PokerPlayer* app) { + if(app->shand[1].index == app->shand[0].index + 1 && + app->shand[2].index == app->shand[1].index + 1 && + app->shand[3].index == app->shand[2].index + 1 && + app->shand[4].index == app->shand[3].index + 1) + return 1; + + /* Ace low straight: Ace, 2, 3, 4, 5 */ + + if(app->shand[4].index == 13 && app->shand[0].index == 1 && app->shand[1].index == 2 && + app->shand[2].index == 3 && app->shand[3].index == 4) + return 1; + + return 0; +} + +/* + Four of a kind: + the middle 3 all match, and the first or last matches those +*/ + +static int four(PokerPlayer* app) { + if((app->shand[1].index == app->shand[2].index && + app->shand[2].index == app->shand[3].index) && + (app->shand[0].index == app->shand[2].index || app->shand[4].index == app->shand[2].index)) + return 1; + + return 0; +} + +/* + Full house: + 3 of a kind and a pair +*/ + +static int full(PokerPlayer* app) { + if(app->shand[0].index == app->shand[1].index && + (app->shand[2].index == app->shand[3].index && app->shand[3].index == app->shand[4].index)) + return 1; + + if(app->shand[3].index == app->shand[4].index && + (app->shand[0].index == app->shand[1].index && app->shand[1].index == app->shand[2].index)) + return 1; + + return 0; +} + +/* + Three of a kind: + it can appear 3 ways +*/ + +static int three(PokerPlayer* app) { + if(app->shand[0].index == app->shand[1].index && app->shand[1].index == app->shand[2].index) + return 1; + + if(app->shand[1].index == app->shand[2].index && app->shand[2].index == app->shand[3].index) + return 1; + + if(app->shand[2].index == app->shand[3].index && app->shand[3].index == app->shand[4].index) + return 1; + + return 0; +} + +/* + Two pair: + it can appear in 3 ways +*/ + +static int twopair(PokerPlayer* app) { + if(((app->shand[0].index == app->shand[1].index) && + (app->shand[2].index == app->shand[3].index)) || + ((app->shand[0].index == app->shand[1].index) && + (app->shand[3].index == app->shand[4].index)) || + ((app->shand[1].index == app->shand[2].index) && + (app->shand[3].index == app->shand[4].index))) + return 1; + + return 0; +} + +/* + Two of a kind (pair), jacks or better + or if the game is Tens or Better, 10s or better. +*/ + +static int two(PokerPlayer* app) { + int min = 10; + + if(app->GameType == 1) min = 9; + + if(app->shand[0].index == app->shand[1].index && app->shand[1].index >= min) return 1; + if(app->shand[1].index == app->shand[2].index && app->shand[2].index >= min) return 1; + if(app->shand[2].index == app->shand[3].index && app->shand[3].index >= min) return 1; + if(app->shand[3].index == app->shand[4].index && app->shand[4].index >= min) return 1; + + return 0; +} + +static int paytable[10] = { + 800, /* royal flush: 800 */ + 50, /* straight flush: 50 */ + 25, /* 4 of a kind: 25 */ + 9, /* full house: 9 */ + 6, /* flush: 6 */ + 4, /* straight: 4 */ + 3, /* 3 of a kind: 3 */ + 2, /* two pair: 2 */ + 1, /* jacks or better: 1 */ + 0 /* nothing */ +}; + +static const char* poker_handname[10] = { + "Royal Flush", + "Straight Flush", + "Four of a Kind", + "Full House", + "Flush", + "Straight", + "Three of a Kind", + "Two Pair", + "Pair", + "Nothing", +}; + +static int recognize(PokerPlayer* app) { + int i, j, f = 0; + int min = 100; + PokerPlayer_card tmp[5]; + int st = 0, fl = 0; + + /* Sort hand into sorted hand (app->shand) */ + + /* make copy of hand */ + for(i = 0; i < 5; i++) tmp[i] = app->hand[i]; + + for(i = 0; i < 5; i++) { + /* put lowest card in hand into next place in app->shand */ + + for(j = 0; j < 5; j++) + if(tmp[j].index <= min) { + min = tmp[j].index; + f = j; + } + + app->shand[i] = tmp[f]; + tmp[f].index = 100; /* larger than any card */ + min = 100; + } + + /* royal and straight flushes, strait, and flush */ + + fl = flush(app); + st = straight(app); + + if(st && fl && app->shand[0].index == 9) return 0; + if(st && fl) return 1; + if(four(app)) return 2; + if(full(app)) return 3; + if(fl) return 4; + if(st) return 5; + if(three(app)) return 6; + if(twopair(app)) return 7; + if(two(app)) return 8; + + /* Nothing */ + + return 9; +} + +void poker_draw_callback(Canvas* canvas, void* ctx) { + PokerPlayer* poker_player = ctx; + furi_check(furi_mutex_acquire(poker_player->model_mutex, FuriWaitForever) == FuriStatusOk); + canvas_clear(canvas); + char buffer[30]; + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + + /* Magic Begins */ + + /* Status Info */ + if(poker_player->GameState != 0 && poker_player->GameState != 4) { + snprintf(buffer, sizeof(buffer), "%d", poker_player->score); + canvas_draw_str_aligned(canvas, 127, 0, AlignRight, AlignTop, buffer); + } + + /* Start of game. Cards are face down, bet can be changed */ + if(poker_player->GameState == 1) { + snprintf(buffer, sizeof(buffer), "Bet:%d", poker_player->bet); + canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, buffer); + snprintf(buffer, sizeof(buffer), "<*> Place Bet"); + canvas_draw_str_aligned(canvas, 0, 9, AlignLeft, AlignTop, buffer); + + for(int i = 0; i < 5; ++i) { + canvas_draw_icon(canvas, 5 + (i * 24), 18, &I_CardBack_22x35); /* 5, 29, 53, 77, 101 */ + } + } + /* Cards are turned face up. Bet is deducted and put in th pot. Show the selector hand */ + else if(poker_player->GameState == 2 || poker_player->GameState == 3) { + snprintf(buffer, sizeof(buffer), "Pot:%d", poker_player->bet); + canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, buffer); + snprintf(buffer, sizeof(buffer), "<*> Select Hold"); + canvas_draw_str_aligned(canvas, 0, 9, AlignLeft, AlignTop, buffer); + + /* Normal or inverse to indicate selection - cards*/ + for(int i = 0; i < 5; ++i) { + poker_player->held[i] ? canvas_draw_rbox(canvas, 5 + (i * 24), 18, 22, 35, 3) : + canvas_draw_rframe(canvas, 5 + (i * 24), 18, 22, 35, 3); + } + + /* Normal or inverse to indicate selection - card suit and value */ + + for(int i = 0; i < 5; ++i) { + poker_player->held[i] ? canvas_set_color(canvas, ColorWhite) : + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 18 + (i * 24), 43, &card_suit[poker_player->hand[i].suit]); + } + + /* Card Value. Profont_22 does not include letters (AJQK), and "10" is too big. These are bitmaps. */ + canvas_set_font(canvas, FontBigNumbers); + + for(int i = 0; i < 5; ++i) { + poker_player->held[i] ? canvas_set_color(canvas, ColorWhite) : + canvas_set_color(canvas, ColorBlack); + if(poker_player->hand[i].index >= 1 && poker_player->hand[i].index <= 8) { + snprintf(buffer, sizeof(buffer), "%s", poker_player->hand[i].sym); + canvas_draw_str_aligned(canvas, 8 + (i * 24), 21, AlignLeft, AlignTop, buffer); + } else { + if(poker_player->hand[i].index >= 9 && poker_player->hand[i].index <= 13) { + canvas_draw_icon( + canvas, 7 + (i * 24), 21, &card_face[poker_player->hand[i].index - 9]); + } + } + } + + /* Draw the Select hand */ + if(poker_player->GameState == 2) { + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 11 + (poker_player->selected * 24), 54, &I_Hand_12x10); + } + } // GameState 2 or 3 + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + if(poker_player->GameState == 3) { + snprintf( + buffer, + sizeof(buffer), + "%s:%ix", + poker_handname[recognize(poker_player)], + paytable[recognize(poker_player)]); + canvas_draw_str_aligned(canvas, 63, 61, AlignCenter, AlignBottom, buffer); + } + if(poker_player->GameState == 0) { + canvas_draw_icon(canvas, 0, 0, &I_Splash_128x64); /* Initial launch */ + } + if(poker_player->GameState == 4) { + /* canvas_draw_icon(canvas, 0, 0, &I_BadEnd_128x64); Just Lost The Game - disabled for now :( */ + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + snprintf(buffer, sizeof(buffer), "%s", "You have run out of money!"); + canvas_draw_str_aligned(canvas, 63, 22, AlignCenter, AlignCenter, buffer); + snprintf(buffer, sizeof(buffer), "%s", "At one point, you had"); + canvas_draw_str_aligned(canvas, 63, 32, AlignCenter, AlignCenter, buffer); + snprintf(buffer, sizeof(buffer), "%d dollars", poker_player->highscore); + canvas_draw_str_aligned(canvas, 63, 42, AlignCenter, AlignCenter, buffer); + } + + furi_mutex_release(poker_player->model_mutex); +} + +void poker_input_callback(InputEvent* input, void* ctx) { + PokerPlayer* poker_player = ctx; + furi_message_queue_put(poker_player->event_queue, input, FuriWaitForever); +} + +PokerPlayer* poker_player_alloc() { + PokerPlayer* poker_player = malloc(sizeof(PokerPlayer)); + + poker_player->score = 1000; + poker_player->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + poker_player->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + poker_player->view_port = view_port_alloc(); + poker_player->selected = 0; + poker_player->GameState = 0; + poker_player->bet = 10; + poker_player->minbet = 10; + poker_player->highscore = 1000; + + playcard( + poker_player); /* Get things rolling before the player gets into the game. This will preload the hand. */ + view_port_draw_callback_set(poker_player->view_port, poker_draw_callback, poker_player); + + view_port_input_callback_set(poker_player->view_port, poker_input_callback, poker_player); + + poker_player->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(poker_player->gui, poker_player->view_port, GuiLayerFullscreen); + + return poker_player; +} + +void poker_player_free(PokerPlayer* poker_player) { + view_port_enabled_set(poker_player->view_port, false); + gui_remove_view_port(poker_player->gui, poker_player->view_port); + furi_record_close(RECORD_GUI); + view_port_free(poker_player->view_port); + furi_message_queue_free(poker_player->event_queue); + furi_mutex_free(poker_player->model_mutex); + + free(poker_player); +} + +int32_t video_poker_app(void* p) { + UNUSED(p); + PokerPlayer* poker_player = poker_player_alloc(); + + InputEvent event; + for(bool processing = true; processing;) { + FuriStatus status = furi_message_queue_get(poker_player->event_queue, &event, 100); + furi_check(furi_mutex_acquire(poker_player->model_mutex, FuriWaitForever) == FuriStatusOk); + if(status == FuriStatusOk) { + if(event.type == InputTypePress) { + switch(event.key) { + case InputKeyUp: + Shake(); + break; + case InputKeyDown: + if(poker_player->GameState == 2) { + playcard(poker_player); + if(check_for_dupes(poker_player) == 0) { + playcard(poker_player); + } + + poker_player->GameState = 3; + } + break; + case InputKeyLeft: + if(poker_player->GameState == 1) { + if(poker_player->bet >= poker_player->minbet + 10) { + poker_player->bet -= 10; + } + } else if(poker_player->selected > 0 && poker_player->GameState == 2) { + poker_player->selected--; + } // Move hand left/right + else if(poker_player->selected == 0 && poker_player->GameState == 2) { + poker_player->selected = 4; //wraparound + } + break; + case InputKeyRight: + if(poker_player->GameState == 1) { + if(poker_player->bet < poker_player->score + 10) { + poker_player->bet += 10; + } + } + if(poker_player->selected < 4 && poker_player->GameState == 2) { + poker_player->selected++; + } // Move hand left/right + else if(poker_player->selected == 4 && poker_player->GameState == 2) { + poker_player->selected = 0; //wraparound + } + break; + case InputKeyOk: + /* close splash screen */ + if(poker_player->GameState == 0) { + poker_player->GameState = 1; + } else if(poker_player->GameState == 1) { + /* Pledge bet. Bet is subtracted here. Original code subtracts it during playcard + but playcard is called multiple times which would otherwise subtract bet + multiple times */ + poker_player->score -= poker_player->bet; + poker_player->GameState = 2; + } else if(poker_player->GameState == 2) { + /* Select or un-select card to be held */ + poker_player->held[poker_player->selected] = + !poker_player + ->held[poker_player->selected]; //cursed and bad pls replace + } else if(poker_player->GameState == 3) { + /* accept your fate */ + if(recognize(poker_player) != 9) { + poker_player->score += + poker_player->bet * paytable[recognize(poker_player)]; + } + poker_player->GameState = 1; + if(poker_player->bet > poker_player->score) { + poker_player->bet = poker_player->score; + } + poker_player->held[0] = 0; + poker_player->held[1] = 0; + poker_player->held[2] = 0; + poker_player->held[3] = 0; + poker_player->held[4] = 0; + if(poker_player->score <= 0) { + /* lost the game */ + poker_player->GameState = 4; + } + playcard(poker_player); // shuffle shuffle + } else if(poker_player->GameState == 4) { + /* escape the summary, return to splash */ + Shake(); + poker_player->selected = 0; + poker_player->GameState = 0; + poker_player->bet = 10; + poker_player->minbet = 10; + poker_player->highscore = 1000; + poker_player->score = 1000; + poker_player->GameState = 0; + } + break; + case InputKeyBack: + /* if game is not over, we should store the game state. */ + processing = false; + break; + default: + break; + } + } + } + furi_mutex_release(poker_player->model_mutex); + view_port_update(poker_player->view_port); + } + + poker_player_free(poker_player); + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/videopoker/pokerIcon.png b/Applications/Official/DEV_FW/source/xMasterX/videopoker/pokerIcon.png new file mode 100644 index 000000000..db5507d0c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/videopoker/pokerIcon.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/README.md b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/README.md new file mode 100644 index 000000000..87a419f90 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/README.md @@ -0,0 +1,50 @@ +# flipperzero_esp8266_deautherv2 +Flipper Zero esp8266 deauther app. + + +Based off the WiFi Marauder App from 0xchocolate. + +https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion + +https://github.com/RogueMaster/flipperzero-firmware-wPlugins/tree/unleashed/applications/wifi_marauder_companion + +uses the Version 2 of the ESP8266 Deauther code. +https://github.com/SpacehuhnTech/esp8266_deauther/tree/v2/esp8266_deauther + +This is done so you can use the original deauther v2 firmware on the esp8266. +you can just flash the latest binary. + +also a shout out to https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module +This is already in the Roguemaster firmware and just needs to be enabled and compiled. unfortunatly I could not get this past the menu when I compiled his deauther source for the nodemcu. Nice menu though. + +I used a nodeMCU board. Wiring is simple. follow the wiring guide on https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module +On mine I connected one G to ground, VIN to 5V, RX to U_TX, TX to U_RX. + +NodeMCU---FlipperZero + +G---------GND + +VIN-------5V + +RX--------U_TX + +TX--------U_RX + + + +Video in action. +https://youtu.be/_RFzZyPkeR0 + +If you want to disable the built in WiFi access and web interface (only use flipper to serial send commands) then select "set webinterface false", "save settings" and "reboot". When it starts back up you wont see the pwned AP any more. + +I installed this into Roguemaster to test. + +git clone --recursive https://github.com/RogueMaster/flipperzero-firmware-wPlugins.git +cd flipperzero-firmware-wPlugins/ + +copy folder into applications. +add "APPS_wifi_deauther", to the meta/application.fam file. + +compile +./fbt resources icons +./fbt updater_package diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/application.fam b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/application.fam new file mode 100644 index 000000000..1d352481b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/application.fam @@ -0,0 +1,12 @@ +App( + appid="ESP8266_Wifi_Deauther_v2", + name="[ESP8266] Deauther v2", + apptype=FlipperAppType.EXTERNAL, + entry_point="wifi_deauther_app", + cdefines=["APP_WIFI_deauther"], + requires=["gui"], + stack_size=1 * 1024, + order=30, + fap_icon="wifi_10px.png", + fap_category="GPIO_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene.c b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene.c new file mode 100644 index 000000000..a974beea9 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene.c @@ -0,0 +1,30 @@ +#include "wifi_deauther_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const wifi_deauther_scene_on_enter_handlers[])(void*) = { +#include "wifi_deauther_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const wifi_deauther_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "wifi_deauther_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const wifi_deauther_scene_on_exit_handlers[])(void* context) = { +#include "wifi_deauther_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers wifi_deauther_scene_handlers = { + .on_enter_handlers = wifi_deauther_scene_on_enter_handlers, + .on_event_handlers = wifi_deauther_scene_on_event_handlers, + .on_exit_handlers = wifi_deauther_scene_on_exit_handlers, + .scene_num = WifideautherSceneNum, +}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene.h b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene.h new file mode 100644 index 000000000..a6ef08553 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) WifideautherScene##id, +typedef enum { +#include "wifi_deauther_scene_config.h" + WifideautherSceneNum, +} WifideautherScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers wifi_deauther_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "wifi_deauther_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "wifi_deauther_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "wifi_deauther_scene_config.h" +#undef ADD_SCENE diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene_config.h b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene_config.h new file mode 100644 index 000000000..5f21cdc50 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(wifi_deauther, start, Start) +ADD_SCENE(wifi_deauther, console_output, ConsoleOutput) +ADD_SCENE(wifi_deauther, text_input, TextInput) diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene_console_output.c b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene_console_output.c new file mode 100644 index 000000000..a3d03a0d1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene_console_output.c @@ -0,0 +1,90 @@ +#include "../wifi_deauther_app_i.h" + +void wifi_deauther_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) { + furi_assert(context); + WifideautherApp* app = context; + + // If text box store gets too big, then truncate it + app->text_box_store_strlen += len; + if(app->text_box_store_strlen >= WIFI_deauther_TEXT_BOX_STORE_SIZE - 1) { + furi_string_right(app->text_box_store, app->text_box_store_strlen / 2); + app->text_box_store_strlen = furi_string_size(app->text_box_store); + } + + // Null-terminate buf and append to text box store + buf[len] = '\0'; + furi_string_cat_printf(app->text_box_store, "%s", buf); + + view_dispatcher_send_custom_event(app->view_dispatcher, WifideautherEventRefreshConsoleOutput); +} + +void wifi_deauther_scene_console_output_on_enter(void* context) { + WifideautherApp* app = context; + + TextBox* text_box = app->text_box; + text_box_reset(app->text_box); + text_box_set_font(text_box, TextBoxFontText); + if(app->focus_console_start) { + text_box_set_focus(text_box, TextBoxFocusStart); + } else { + text_box_set_focus(text_box, TextBoxFocusEnd); + } + if(app->is_command) { + furi_string_reset(app->text_box_store); + app->text_box_store_strlen = 0; + if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) { + const char* help_msg = "For app support/feedback,\nreach out to\n"; + furi_string_cat_str(app->text_box_store, help_msg); + app->text_box_store_strlen += strlen(help_msg); + } + + if(app->show_stopscan_tip) { + const char* help_msg = "Press BACK to send stopscan\n"; + furi_string_cat_str(app->text_box_store, help_msg); + app->text_box_store_strlen += strlen(help_msg); + } + } else { // "View Log" menu action + text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); + } + + scene_manager_set_scene_state(app->scene_manager, WifideautherSceneConsoleOutput, 0); + view_dispatcher_switch_to_view(app->view_dispatcher, WifideautherAppViewConsoleOutput); + + // Register callback to receive data + wifi_deauther_uart_set_handle_rx_data_cb( + app->uart, wifi_deauther_console_output_handle_rx_data_cb); // setup callback for rx thread + + // Send command with newline '\n' + if(app->is_command && app->selected_tx_string) { + wifi_deauther_uart_tx( + (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); + wifi_deauther_uart_tx((uint8_t*)("\n"), 1); + } +} + +bool wifi_deauther_scene_console_output_on_event(void* context, SceneManagerEvent event) { + WifideautherApp* app = context; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + text_box_set_text(app->text_box, furi_string_get_cstr(app->text_box_store)); + consumed = true; + } else if(event.type == SceneManagerEventTypeTick) { + consumed = true; + } + + return consumed; +} + +void wifi_deauther_scene_console_output_on_exit(void* context) { + WifideautherApp* app = context; + + // Unregister rx callback + wifi_deauther_uart_set_handle_rx_data_cb(app->uart, NULL); + + // Automatically stop the scan when exiting view + if(app->is_command) { + wifi_deauther_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); + } +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene_start.c b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene_start.c new file mode 100644 index 000000000..92b80b2cf --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene_start.c @@ -0,0 +1,172 @@ +#include "../wifi_deauther_app_i.h" + +// For each command, define whether additional arguments are needed +// (enabling text input to fill them out), and whether the console +// text box should focus at the start of the output or the end +typedef enum { NO_ARGS = 0, INPUT_ARGS, TOGGLE_ARGS } InputArgs; + +typedef enum { FOCUS_CONSOLE_END = 0, FOCUS_CONSOLE_START, FOCUS_CONSOLE_TOGGLE } FocusConsole; + +#define SHOW_STOPSCAN_TIP (true) +#define NO_TIP (false) + +#define MAX_OPTIONS (6) +typedef struct { + const char* item_string; + const char* options_menu[MAX_OPTIONS]; + int num_options_menu; + const char* actual_commands[MAX_OPTIONS]; + InputArgs needs_keyboard; + FocusConsole focus_console; + bool show_stopscan_tip; +} WifideautherItem; + +// NUM_MENU_ITEMS defined in wifi_deauther_app_i.h - if you add an entry here, increment it! +const WifideautherItem MenuItems[NUM_MENU_ITEMS] = { + {"View Log from", {"start", "end"}, 2, {}, NO_ARGS, FOCUS_CONSOLE_TOGGLE, NO_TIP}, + {"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, + {"Stop", {""}, 1, {"stop all"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, + {"Scan", + {"All", "SSIDs", "Stations"}, + 3, + {"scan", "scan aps", "scan stations"}, + NO_ARGS, + FOCUS_CONSOLE_END, + NO_TIP}, + {"Select", + {"All", "SSIDs", "Stations"}, + 3, + {"select all", "select aps", "select stations"}, + INPUT_ARGS, + FOCUS_CONSOLE_END, + NO_TIP}, + {"Deselect", + {"All", "SSIDs", "Stations"}, + 3, + {"deselect all", "deselect aps", "deselect stations"}, + INPUT_ARGS, + FOCUS_CONSOLE_END, + NO_TIP}, + {"Show", + {"SSIDs", "Stations", "All", "Selected"}, + 4, + {"show ap", "show station", "show all", "show selected"}, + NO_ARGS, + FOCUS_CONSOLE_END, + NO_TIP}, + {"Attack", + {"deauth", "deauthall", "beacon", "probe"}, + 4, + {"attack deauth", "attack deauthall", "attack beacon", "attack probe"}, + NO_ARGS, + FOCUS_CONSOLE_END, + SHOW_STOPSCAN_TIP}, + {"Settings", + {"Get", "Remove AP", "Set SSID", "Set Pass", "Save"}, + 5, + {"get settings", + "set webinterface false", + "set ssid: pwned", + "set password: deauther", + "save settings"}, + INPUT_ARGS, + FOCUS_CONSOLE_END, + NO_TIP}, + {"Sysinfo", {""}, 1, {"sysinfo"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, + {"Reboot", {""}, 1, {"reboot"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, +}; + +static void wifi_deauther_scene_start_var_list_enter_callback(void* context, uint32_t index) { + furi_assert(context); + WifideautherApp* app = context; + if(app->selected_option_index[index] < MenuItems[index].num_options_menu) { + app->selected_tx_string = + MenuItems[index].actual_commands[app->selected_option_index[index]]; + } + app->is_command = (1 <= index); + app->is_custom_tx_string = false; + app->selected_menu_index = index; + app->focus_console_start = (MenuItems[index].focus_console == FOCUS_CONSOLE_TOGGLE) ? + (app->selected_option_index[index] == 0) : + MenuItems[index].focus_console; + app->show_stopscan_tip = MenuItems[index].show_stopscan_tip; + + bool needs_keyboard = (MenuItems[index].needs_keyboard == TOGGLE_ARGS) ? + (app->selected_option_index[index] != 0) : + MenuItems[index].needs_keyboard; + if(needs_keyboard) { + view_dispatcher_send_custom_event(app->view_dispatcher, WifideautherEventStartKeyboard); + } else { + view_dispatcher_send_custom_event(app->view_dispatcher, WifideautherEventStartConsole); + } +} + +static void wifi_deauther_scene_start_var_list_change_callback(VariableItem* item) { + furi_assert(item); + + WifideautherApp* app = variable_item_get_context(item); + furi_assert(app); + + const WifideautherItem* menu_item = &MenuItems[app->selected_menu_index]; + uint8_t item_index = variable_item_get_current_value_index(item); + furi_assert(item_index < menu_item->num_options_menu); + variable_item_set_current_value_text(item, menu_item->options_menu[item_index]); + app->selected_option_index[app->selected_menu_index] = item_index; +} + +void wifi_deauther_scene_start_on_enter(void* context) { + WifideautherApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + + variable_item_list_set_enter_callback( + var_item_list, wifi_deauther_scene_start_var_list_enter_callback, app); + + VariableItem* item; + for(int i = 0; i < NUM_MENU_ITEMS; ++i) { + item = variable_item_list_add( + var_item_list, + MenuItems[i].item_string, + MenuItems[i].num_options_menu, + wifi_deauther_scene_start_var_list_change_callback, + app); + if(MenuItems[i].num_options_menu) { + variable_item_set_current_value_index(item, app->selected_option_index[i]); + variable_item_set_current_value_text( + item, MenuItems[i].options_menu[app->selected_option_index[i]]); + } + } + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, WifideautherSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, WifideautherAppViewVarItemList); +} + +bool wifi_deauther_scene_start_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + WifideautherApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == WifideautherEventStartKeyboard) { + scene_manager_set_scene_state( + app->scene_manager, WifideautherSceneStart, app->selected_menu_index); + scene_manager_next_scene(app->scene_manager, WifideautherAppViewTextInput); + } else if(event.event == WifideautherEventStartConsole) { + scene_manager_set_scene_state( + app->scene_manager, WifideautherSceneStart, app->selected_menu_index); + scene_manager_next_scene(app->scene_manager, WifideautherAppViewConsoleOutput); + } + consumed = true; + } else if(event.type == SceneManagerEventTypeTick) { + app->selected_menu_index = variable_item_list_get_selected_item_index(app->var_item_list); + consumed = true; + } + + return consumed; +} + +void wifi_deauther_scene_start_on_exit(void* context) { + WifideautherApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene_text_input.c b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene_text_input.c new file mode 100644 index 000000000..339b2f2a3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/scenes/wifi_deauther_scene_text_input.c @@ -0,0 +1,72 @@ +#include "../wifi_deauther_app_i.h" + +void wifi_deauther_scene_text_input_callback(void* context) { + WifideautherApp* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, WifideautherEventStartConsole); +} + +void wifi_deauther_scene_text_input_on_enter(void* context) { + WifideautherApp* app = context; + + if(false == app->is_custom_tx_string) { + // Fill text input with selected string so that user can add to it + size_t length = strlen(app->selected_tx_string); + furi_assert(length < WIFI_deauther_TEXT_INPUT_STORE_SIZE); + bzero(app->text_input_store, WIFI_deauther_TEXT_INPUT_STORE_SIZE); + strncpy(app->text_input_store, app->selected_tx_string, length); + + // Add space - because flipper keyboard currently doesn't have a space + app->text_input_store[length] = ' '; + app->text_input_store[length + 1] = '\0'; + app->is_custom_tx_string = true; + } + + // Setup view + TextInput* text_input = app->text_input; + // Add help message to header + if(0 == strncmp("ssid -a -g", app->selected_tx_string, strlen("ssid -a -g"))) { + text_input_set_header_text(text_input, "Enter # SSIDs to generate"); + } else if(0 == strncmp("ssid -a -n", app->selected_tx_string, strlen("ssid -a -n"))) { + text_input_set_header_text(text_input, "Enter SSID name to add"); + } else if(0 == strncmp("ssid -r", app->selected_tx_string, strlen("ssid -r"))) { + text_input_set_header_text(text_input, "Remove target from SSID list"); + } else if(0 == strncmp("select -a", app->selected_tx_string, strlen("select -a"))) { + text_input_set_header_text(text_input, "Add target from AP list"); + } else if(0 == strncmp("select -s", app->selected_tx_string, strlen("select -s"))) { + text_input_set_header_text(text_input, "Add target from SSID list"); + } else { + text_input_set_header_text(text_input, "Add command arguments"); + } + text_input_set_result_callback( + text_input, + wifi_deauther_scene_text_input_callback, + app, + app->text_input_store, + WIFI_deauther_TEXT_INPUT_STORE_SIZE, + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, WifideautherAppViewTextInput); +} + +bool wifi_deauther_scene_text_input_on_event(void* context, SceneManagerEvent event) { + WifideautherApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == WifideautherEventStartConsole) { + // Point to custom string to send + app->selected_tx_string = app->text_input_store; + scene_manager_next_scene(app->scene_manager, WifideautherAppViewConsoleOutput); + consumed = true; + } + } + + return consumed; +} + +void wifi_deauther_scene_text_input_on_exit(void* context) { + WifideautherApp* app = context; + + text_input_reset(app->text_input); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_10px.png b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_10px.png new file mode 100644 index 000000000..c13534660 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_10px.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_app.c b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_app.c new file mode 100644 index 000000000..28fb28d88 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_app.c @@ -0,0 +1,106 @@ +#include "wifi_deauther_app_i.h" + +#include +#include +#include + +static bool wifi_deauther_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + WifideautherApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool wifi_deauther_app_back_event_callback(void* context) { + furi_assert(context); + WifideautherApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void wifi_deauther_app_tick_event_callback(void* context) { + furi_assert(context); + WifideautherApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +WifideautherApp* wifi_deauther_app_alloc() { + WifideautherApp* app = malloc(sizeof(WifideautherApp)); + + app->gui = furi_record_open(RECORD_GUI); + + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&wifi_deauther_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, wifi_deauther_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, wifi_deauther_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, wifi_deauther_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + WifideautherAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + for(int i = 0; i < NUM_MENU_ITEMS; ++i) { + app->selected_option_index[i] = 0; + } + + app->text_box = text_box_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, WifideautherAppViewConsoleOutput, text_box_get_view(app->text_box)); + app->text_box_store = furi_string_alloc(); + furi_string_reserve(app->text_box_store, WIFI_deauther_TEXT_BOX_STORE_SIZE); + + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, WifideautherAppViewTextInput, text_input_get_view(app->text_input)); + + scene_manager_next_scene(app->scene_manager, WifideautherSceneStart); + + return app; +} + +void wifi_deauther_app_free(WifideautherApp* app) { + furi_assert(app); + + // Views + view_dispatcher_remove_view(app->view_dispatcher, WifideautherAppViewVarItemList); + view_dispatcher_remove_view(app->view_dispatcher, WifideautherAppViewConsoleOutput); + view_dispatcher_remove_view(app->view_dispatcher, WifideautherAppViewTextInput); + text_box_free(app->text_box); + furi_string_free(app->text_box_store); + text_input_free(app->text_input); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + wifi_deauther_uart_free(app->uart); + + // Close records + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t wifi_deauther_app(void* p) { + furi_hal_power_enable_otg(); + furi_delay_ms(600); + UNUSED(p); + WifideautherApp* wifi_deauther_app = wifi_deauther_app_alloc(); + + wifi_deauther_app->uart = wifi_deauther_uart_init(wifi_deauther_app); + + view_dispatcher_run(wifi_deauther_app->view_dispatcher); + + wifi_deauther_app_free(wifi_deauther_app); + furi_hal_power_disable_otg(); + + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_app.h b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_app.h new file mode 100644 index 000000000..bb2f6fbfb --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_app.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct WifideautherApp WifideautherApp; + +#ifdef __cplusplus +} +#endif diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_app_i.h b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_app_i.h new file mode 100644 index 000000000..bab52f385 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_app_i.h @@ -0,0 +1,69 @@ +#pragma once + +#include "wifi_deauther_app.h" +#include "scenes/wifi_deauther_scene.h" +#include "wifi_deauther_custom_event.h" +#include "wifi_deauther_uart.h" + +#include +#include +#include +#include +#include +#include + +#define NUM_MENU_ITEMS (11) + +#define WIFI_deauther_TEXT_BOX_STORE_SIZE (4096) +#define WIFI_deauther_TEXT_INPUT_STORE_SIZE (512) + +struct WifideautherApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + + char text_input_store[WIFI_deauther_TEXT_INPUT_STORE_SIZE + 1]; + FuriString* text_box_store; + size_t text_box_store_strlen; + TextBox* text_box; + TextInput* text_input; + //Widget* widget; + + VariableItemList* var_item_list; + + WifideautherUart* uart; + int selected_menu_index; + int selected_option_index[NUM_MENU_ITEMS]; + const char* selected_tx_string; + bool is_command; + bool is_custom_tx_string; + bool focus_console_start; + bool show_stopscan_tip; +}; + +// Supported commands: +// https://github.com/justcallmekoko/ESP32deauther/wiki/cli +// Scan +// -> If list is empty, then start a new scanap. (Tap any button to stop.) +// -> If there's a list, provide option to rescan and dump list of targets to select. +// -> Press BACK to go back to top-level. +// Attack +// -> Beacon +// -> Deauth +// -> Probe +// -> Rickroll +// Sniff +// -> Beacon +// -> Deauth +// -> ESP +// -> PMKID +// -> Pwnagotchi +// Channel +// Update +// Reboot + +typedef enum { + WifideautherAppViewVarItemList, + WifideautherAppViewConsoleOutput, + WifideautherAppViewTextInput, +} WifideautherAppView; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_custom_event.h b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_custom_event.h new file mode 100644 index 000000000..142961b1d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_custom_event.h @@ -0,0 +1,7 @@ +#pragma once + +typedef enum { + WifideautherEventRefreshConsoleOutput = 0, + WifideautherEventStartConsole, + WifideautherEventStartKeyboard, +} WifideautherCustomEvent; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_uart.c b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_uart.c new file mode 100644 index 000000000..434ccfd97 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_uart.c @@ -0,0 +1,98 @@ +#include "wifi_deauther_app_i.h" +#include "wifi_deauther_uart.h" + +#include + +#define UART_CH (FuriHalUartIdUSART1) +#define BAUDRATE (115200) + +struct WifideautherUart { + WifideautherApp* app; + FuriThread* rx_thread; + StreamBufferHandle_t rx_stream; + uint8_t rx_buf[RX_BUF_SIZE + 1]; + void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context); +}; + +typedef enum { + WorkerEvtStop = (1 << 0), + WorkerEvtRxDone = (1 << 1), +} WorkerEvtFlags; + +void wifi_deauther_uart_set_handle_rx_data_cb( + WifideautherUart* uart, + void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context)) { + furi_assert(uart); + uart->handle_rx_data_cb = handle_rx_data_cb; +} + +#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone) + +void wifi_deauther_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { + WifideautherUart* uart = (WifideautherUart*)context; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + if(ev == UartIrqEventRXNE) { + xStreamBufferSendFromISR(uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken); + furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtRxDone); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } +} + +static int32_t uart_worker(void* context) { + WifideautherUart* uart = (void*)context; + + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + if(events & WorkerEvtStop) break; + if(events & WorkerEvtRxDone) { + size_t len = xStreamBufferReceive(uart->rx_stream, uart->rx_buf, RX_BUF_SIZE, 0); + if(len > 0) { + if(uart->handle_rx_data_cb) uart->handle_rx_data_cb(uart->rx_buf, len, uart->app); + } + } + } + + vStreamBufferDelete(uart->rx_stream); + + return 0; +} + +void wifi_deauther_uart_tx(uint8_t* data, size_t len) { + furi_hal_uart_tx(UART_CH, data, len); +} + +WifideautherUart* wifi_deauther_uart_init(WifideautherApp* app) { + WifideautherUart* uart = malloc(sizeof(WifideautherUart)); + + uart->app = app; + uart->rx_stream = xStreamBufferCreate(RX_BUF_SIZE, 1); + uart->rx_thread = furi_thread_alloc(); + furi_thread_set_name(uart->rx_thread, "WifideautherUartRxThread"); + furi_thread_set_stack_size(uart->rx_thread, 1024); + furi_thread_set_context(uart->rx_thread, uart); + furi_thread_set_callback(uart->rx_thread, uart_worker); + + furi_thread_start(uart->rx_thread); + + furi_hal_console_disable(); + furi_hal_uart_set_br(UART_CH, BAUDRATE); + furi_hal_uart_set_irq_cb(UART_CH, wifi_deauther_uart_on_irq_cb, uart); + + return uart; +} + +void wifi_deauther_uart_free(WifideautherUart* uart) { + furi_assert(uart); + + furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtStop); + furi_thread_join(uart->rx_thread); + furi_thread_free(uart->rx_thread); + + furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); + furi_hal_console_enable(); + + free(uart); +} \ No newline at end of file diff --git a/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_uart.h b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_uart.h new file mode 100644 index 000000000..534c2fdf7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wifi_deauther/wifi_deauther_uart.h @@ -0,0 +1,14 @@ +#pragma once + +#include "furi_hal.h" + +#define RX_BUF_SIZE (320) + +typedef struct WifideautherUart WifideautherUart; + +void wifi_deauther_uart_set_handle_rx_data_cb( + WifideautherUart* uart, + void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context)); +void wifi_deauther_uart_tx(uint8_t* data, size_t len); +WifideautherUart* wifi_deauther_uart_init(WifideautherApp* app); +void wifi_deauther_uart_free(WifideautherUart* uart); diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/LICENSE new file mode 100644 index 000000000..95e544a06 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 BlueChip + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/README.md b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/README.md new file mode 100644 index 000000000..fed7a1493 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/README.md @@ -0,0 +1,233 @@ +# [FlipperZero] Wii Extension Controller Protocol Analyser +This Protocol Analyser offers a full Test and Calibrate system for Wii Extension Controllers. + +__Disclaimer:__ *Use of this plugin, and notably connecting an Extension Controller to the FlipperZero is performed entirely at your own risk.* + +# Notes +This plugin has (todate) only been tested with official Nintendo Nunchucks and Classic Controllers - namely Nunchucks and Classic Controllers. + +# Encryption +This plugin has SOME code to handle encryption, but it it unused, untested, and some of it is known to un-work. + +This plugin (currently) only works with Extension Controllers which implement the encryption-bypass strategy. IE. `i2c_write(0xf0, 0x55) ; i2c_write(0xfb, 0x00)` + +If you need this functionality, either raise an Issue or, better still, a Pull Request. + +# Screen: SPLASH +
+The SPLASH Screen is displayed when the Plugin starts. It can be cleared by pressing any key, else it will auto-clear after 3.5 seconds. + +# Screen: WAIT +   

+The WAIT screen will display which pins you need to connect between the flipper and the Wii Extension Controller. + +__Disclaimer:__ Use of this plugin, and notably connecting the Controller to the FlipperZero is performed entirely at your own risk. + +Looking in to the exposed side of the Extension Controller plug, with the notch on the bottom + +| EC Pin # | EC Position | EC Pin ID | Pin Function | FZ GPIO Pin Name | FZ GPIO Pin # | +| :---: | :---: | :---: | :---: | :---: | :---: | +| 1 | top-left | +3v3 | Power | 3v3 | 9 | +| 2 | bottom-left | SCL | i2c clock | C0 | 16 | +| 3 | top-centre | EN | ¿detect? | | | +| 4 | bottom-centre | -x- | -none- | | | +| 5 | top-right | SDA | i2c data | C1 | 15 | +| 6 | bottom-right | Gnd | Power | Gnd | 18 | + +Keys: +* Left - Show splash screen +* Back - exit plugin + +The easiest way to connect a Wii Extension Controller to a FlipperZero is arguably with a ["WiiChuck"](https://www.ebay.co.uk/sch/?_nkw=wiichuck) or a ["Nunchucky"](https://www.solarbotics.com/product/31040)

+ + + + + +
WiiChuckNunchucky
+ +### ** WARNING ** +Neither the WiiChuck, nor the Nunchucky have a pin polarisation mechanism.
+If you plug the adaptor in the wrong way around you WILL apply voltage to the Controller the wrong way round!!
+I have no idea if THIS WILL PERMANENTLY KILL THE CONTROLLER ...Who wants to try it? + +On all the WiiChucks I have seen: +* The WiiChuck has THREE connectors on one side, and TWO connectors on the other. +* The side with TWO connectors should go against the side of the Controller plug with the big indent. +``` ++-------------+ +| _________ | +| | = = = | | +| |_=_____=_| | <-- notice missing pin +| ___ | +| | | | <-- notice indent ++----+ +----+ +``` +
+ +...BUT I *highly* recommend you check the pins on your adaptor to make sure everything goes well. + +I believe the unconnected pin on the top is a "presence detect" function, but I have not (yet) verified this.
+This feature is NOT required by this plugin, as the detection is performed by means of an i2c handshake. + +When a device is connected it will be immediately recognised. If it is not, either: +* The Controller is not correctly connected
+...This may be as simple as a broken wire. +* The controller board in the Controller is faulty.
+...Repair of which is beyond the scope of this document. + +To get the list of "known" Controllers, run `./info.sh`
+As of writing this, that returns: +```c +[PID_UNKNOWN ] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "Unknown Perhipheral", SCENE_DUMP, +[PID_NUNCHUCK ] = { {0x00, 0x00, 0xA4, 0x20, 0x00, 0x00}, "Nunchuck", SCENE_NUNCHUCK, +[PID_CLASSIC ] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x01}, "Classic Controller", SCENE_CLASSIC, +[PID_BALANCE ] = { {0x00, 0x00, 0xA4, 0x20, 0x04, 0x02}, "Balance Board", SCENE_DUMP, +[PID_GH_GUITAR ] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x03}, "Guitar Hero Guitar", SCENE_DUMP, +[PID_GH_DRUMS ] = { {0x01, 0x00, 0xA4, 0x20, 0x01, 0x03}, "Guitar Hero World Tour Drums", SCENE_DUMP, +[PID_TURNTABLE ] = { {0x03, 0x00, 0xA4, 0x20, 0x01, 0x03}, "DJ Hero Turntable", SCENE_DUMP, +[PID_TAIKO_DRUMS] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x11}, "Taiko Drum Controller)", SCENE_DUMP, + +``` + +You can see that there are EIGHT known devices. One is the default for an unknown controller; SEVEN devices are known by name; and TWO (of those seven) have bespoke "scenes" (ie. SCENE_NUNCHUCK & SCENE_CLASSIC). + +# Screen: NUNCHUCK - MAIN +
+When you connect a Nunchuck, you will see a screen displaying: +* Accelerometer{X,Y,Z} values +* Joystick{X,Y} values +* Joystick graphic +* Button{C,Z} + +Keys: +* Left - Go to the DUMP screen +* Right - Go to the NUNCHUCK_ACC accelerometers screen +* Up/Down/OK - [qv. Peak Meters] +* Short-Back - Reset controller +* Long-Back - Exit plugin + +# Screen: NUNCHUCK - ACCELEROMETERS + +   
+ +| Axis | Movement | Lower | Higher | +| :---: | :---: | :---: | :---: | +| X | Left / Right | Left | Right | +| Y | Fwd / Bkwd | Fwd | Bkwd | +| Z | Down / Up | Down | Up | + +* Movement in the direction of an axis changes that axis reading +* Twisting/tilting around an axis changes the other two readings +* EG. + * Move left (along the X axis) will effect X + * Turn left (a rotation around the Y axis) will effect X and Z + +Keys: +* Left - go to the main NUNCHUCK screen +* Up + * Auto-Pause Disabled --> Enable Auto-Pause + * Paused at the end of a page --> Restart scanner + * Running with Auto-Pause Enabled --> Disable Auto-Pause +* Nunchuck-Z - Toggle pause +* Nunchuck-C - Toggle auto-pause +* Long-OK - Enter Software Calibration mode [qv. Calibration] + * Calibration mode on the Accelerometer screen will ONLY calibrate the accelerometer +* Short-OK - Leave Software Calibration mode *and* Calibrate CENTRE position(s) +* Short-Back - Reset controller +* Long-Back - Exit plugin + +NB. Code DOES exist to scroll the display, but the LCD refresh rate is too low, and it looks awful + +# Screen: CLASSIC +
+When you connect a Classic Controller [Pro], you will see a screen displaying a Classic Controller +* The Classic Controller will animate in line with controller events +* The scan rate is set to 30fps, but in reality there is a bit of lag with the LCD screen, so YMMV. + +Keys: +* Left - go to the DUMP screen +* Right - show analogue readings (Left to hide them again) +* Up/Down/OK - [qv. Peak Meters] +* Short-Back - Reset controller +* Long-Back - Exit plugin + +# Screen: DUMP +
+The Dump screen will show you the raw readings from the device.
+If you connect a device which does not have a bespoke `_decode()` function (etc.), you will see (only) this screen. +* SID - String ID - human-readable name (from the `info` table) +* PID - Peripheral ID - The 6 bytes which identify the device. +* Cal - Calibration data - 16 bytes +* The bottom row of hex shows the SIX bytes of Controller data + * Below each hex digit is the binary representation of that digit + * By example. With a Nunchuck connected, click the Z button, and watch the bit on the far right + +Keys: +* Right - return to controller-specific screen (if there is one) +* Short-Back - Reset controller +* Long-Back - Exit plugin + +# Peak Meters (Calibration values) + +On any Controller-specific screen with a Peak/Trough menu displayed: +* Up - [toggle] only show peak values +* Down - [toggle] only show trough values +* Long-OK - Enter Software Calibration mode [qv. Calibration] +* Short-OK - Leave Software Calibration mode / Calibrate CENTRE position(s) + +# Calibration +
+ +Digital buttons do NOT require Calibration. + +Some controllers have Calibration data which is calculated at the factory, and stored in memory (¿OTP?) on the Controller. +EG. Classic Controller HAS Calibration data, but Classic Controller Pro does NOT! + +Each device has a different way to interpret the Calibration Data.
+EG. A Nunchuck has one joystick, and an accelerometer ...whereas a Classic Controller has 2 joysticks and 2 analogue buttons. + +I have personally found the calibration data to be inaccurate (when compared to actual readings), I guess Controllers drift over the years‽ +If the factory-values LIMIT movement, this is easily resolved - by expanding them on-the-fly.
+BUT, I have seen Controllers with factory calibration data that suggests the limits are FURTHER than the joystick can reach ...and this requires a full re-calibration of the Controller! + +Probably the best way to calibrate is to: +* Take a/some reading(s) while the Controller is 'at rest', IE. perfectly still and level. +* Move the Controller to all extremes and store the extreme {peak/trough} values. + +Nintendo (allegedly) take the 'at rest' reading immediately after the Controller is connected, and a 're-calibration' can be performed at any time by pressing {`A`, `B`, `+`, `-`} at the same time, for at least 3 seconds. Although I have no details on what this actually does. + +### This tool calibrates as such: +* When the Controller is first recognised + * The factory Calibration data is used to decide the Centre/Middle position and extreme values (eg. far-left & far-right) for each analogue Control +* Long-OK button press (on the FlipperZero) ...Do NOT touch ANY of the analogue controllers while you are pressing Long-OK + * Start the calibrate button flashing + * Take the current reading as the Centre position + * Set the range limits to "no range" + * You must now move the Control between its extremes, so the code can work out the new Calibration/range/peak+trough values + * When done, press Short-OK to end Software Calibration mode +* Short-OK button press (on the FlipperZero) ...Do NOT touch ANY of the analogue controllers while you are pressing Short-OK + * Stop the calibrate button flashing + * Calibrate the centre position of all analogue controls (accelerometers not supported (yet)) + +# Screen: DEBUG +
+On any screen (except SPLASH) you may press Long-Down to enter Debug mode. + +You can (at any time) attach to the FlipperZero (via USB) with a serial console {`minicom`, `putty`, whatever} and start the `log` function to see the debug messages. + +When you enter the DEBUG screen, the real-time scanner will be stopped. And the following keys made available: +* Up - Attempt to initialise the attached Controller +* OK - Take a reading from the attached Controller +* Long-Down - Restart the real-time scanner and return to the WAIT screen + +You can limit the messages at compile-time [see `./info.sh`], or at runtime [FZ->Settings->System->LogLevel]
+ +[This is probably irrelevant since the introduction of FAP support]
+If you have memory issues, limiting the messages at compile-time will make the plugin smaller.
+But (¿obviously?) the more you limit the messsages, the less debug information will be sent to the logger. + +# TODO + +* FZ Bug: At the time of writing this, there are problems with the i2c FZ functions [qv `i2c_workaround.c`] + diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/README.txt b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/README.txt new file mode 100644 index 000000000..e7ebe7a4c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/README.txt @@ -0,0 +1,67 @@ + ,-------. +---( Files )--- + `-------' + + README.md - User Manual : Body [github markdown] + _images/ - User Manual : Images + _images/GIMP/ - User Manual : GIMP image masters + + LICENSE - Tech Docs : MIT Licence file + README.txt - Tech Docs : Dev notes + notes.txt - Tech Docs : Random dev notes + info.sh - Tech Docs : Retrieve info from source code + + application.fam - FAP : Header file + WiiEC.png - FAP : Icon {10x10} + + gfx/ - Analyser : Images [generated by bc_image_tool] + wii_anal.c|h - Analyser : Main application + wii_anal_ec.c|h - Analyser : Extension controller actions + wii_anal_keys.c|h - Analyser : Keyboard handling + wii_anal_lcd.c|h - Analyser : LCD handling + + i2c_workaround.h - Temporary workaround for i2c bug in FZ code + err.h - Errors + bc_logging.h - Logging macros - especially LOG_LEVEL + + wii_i2c.c|h - i2c functionality + + wii_ec.c|h - Extension Controller basic functions + wii_ec_macros.h - Bespoke Extension Controller handy-dandy MACROs + wii_ec_classic.c|h - EC: Classic Controller Pro scene + wii_ec_nunchuck.c|h - EC: Nunchuck scene + wii_ec_udraw.c|h - EC: UDraw scene - not written + + ,----------------------------------. +---( Adding a new Extension Controller )--- + `----------------------------------' + +//! I'll finish this when I write the UDraw code + +Create a new Extension Controller called "mydev" + +Create wii_ec_mydev.c and wii_ec_mydev.h + +In wii_ec_mydev.c|h + Create the functions [& prototypes] + bool mydev_init (wiiEC_t* const) ; // Additional initialisation code + void mydev_decode (wiiEC_t* const) ; // Decode controller input data + void mydev_msg (wiiEC_t* const, FuriMessageQueue* const) ; // Put event messages in the event queue + void mydev_calib (wiiEC_t* const, ecCalib_t) ; // Controller calibration function + void mydev_show (Canvas* const, state_t* const) ; // Scene LCD display + bool mydev_key (const eventMsg_t* const, state_t* const) ; // Scene key controls + +In wii_ec.h + Include the new header + #include "wii_ec_mydev.h" + Add a perhipheral id to enum ecPid + PID_MYDEV + +In wii_anal.h + As a scene name to enum scene + SCENE_MYDEV + +In wii_ec.c + Add the device definition to the ecId[] array + [PID_MYDEV] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "My Device", SCENE_MYDEV, + mydev_init, mydev_decode, mydev_msg, mydev_calib, mydev_show, mydev_key }, diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/WiiEC.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/WiiEC.png new file mode 100644 index 000000000..6e1afcb0c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/WiiEC.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/LICENSE b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/LICENSE new file mode 100644 index 000000000..95e544a06 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 BlueChip + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/README b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/README new file mode 100644 index 000000000..979605a08 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/README @@ -0,0 +1,30 @@ +1. Prepare the image + a. Open your *black and white* image in GIMP + b. File -> Export As + filename: EXAMPLE.c + Type : "C source code" + [Export] + prefixed name: gimp_image + Comment : + [x] Use GLib types + [ ] <> + Opacity : 100% + [Export] + +2. Prepare conversion tool [stored in (eg.) /path/] + a. cp _convert*.* /path/ + b. cp EXAMPLE.c /path/ + +3. Run the conversion tool + a. cd /path/ + b. ./_convert.sh EXAMPLE.c + +4. All being well, you will see an ascii version of your image. + If not, then you're gonna have to submit a bug report + +5. You should now have a directory called img_/ + In that directory should be + img_EXAMPLE.c - The data for your new image + img_*.c - The data for other images + images.h - A header for ALL images that have been created in this directory + images.c - A sample FlipperZero show() function [not optimised] diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert.c new file mode 100644 index 000000000..267985e8d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include + +int main (int argc, char* argv[]) +{ + const unsigned char* pp = NULL; + uint32_t pix = 0; + int bit = 0; + + uint8_t b = 0; + uint8_t bcnt = 0; + + unsigned int lcnt = 0; + static const int lmax = 16; // max hex values per line + + uint8_t* buf = NULL; + uint8_t* bp = NULL; + unsigned int blen = 0; + + uint8_t* cmp = NULL; + uint8_t* cp = NULL; + unsigned int clen = 0; + uint8_t ctag = 0xFF; + uint32_t tag[256] = {0}; + uint32_t tmax = UINT32_MAX; + + unsigned int x, y, z; + + const char* name = argv[1]; + FILE* fh = fopen(argv[2], "wb"); + + uint32_t white = 0xFF; + + int rv = 0; // assume success + + // allocate buffers + blen = ((img.w * img.h) +0x7) >>3; + bp = (buf = calloc(blen +1, 1)); + cp = (cmp = calloc(blen +4, 1)); + + // sanity check + if (!fh || !buf || !cmp) { + printf("! fopen() or malloc() fail.\n"); + rv = 255; + goto bail; + } + + // Find white value + for (x = 1; x < img.bpp; x++) + white = (white << 8) | 0xFF ; + + // build bit pattern + // create the comment as we go + for (pp = img.b, y = 0; y < img.h; y++) { + fprintf(fh, "// "); + for (x = 0; x < img.w; x++) { + // read pixel + for (pix = 0, z = 0; z < img.bpp; pix = (pix << 8) | *pp++, z++) ; + // get bit and draw + if (pix < white) { + b = (b << 1) | 1; + fprintf(fh, "##"); + } else { + b <<= 1; + fprintf(fh, ".."); + } + // got byte + if ((++bcnt) == 8) { + *bp++ = b; + tag[b]++; + bcnt = (b = 0); + } + } + fprintf(fh, "\n"); + } + fprintf(fh, "\n"); + // padding + if (bcnt) { + b <<= (bcnt = 8 - bcnt); + *bp++ = b; + tag[b]++; + } + // Kill the compression + *bp = ~bp[-1]; // https://youtube.com/clip/Ugkx-JZIr16hETy7hz_H6yIdKPtxVe8C5w_V + + // Byte run length compression + // Find a good tag + for (x = 0; tmax && (x < 256); x++) { + if (tag[x] < tmax) { + tmax = tag[x]; + ctag = x; + } + } + + // compress the data + for (bp = buf, x = 0; (clen < blen) && (x < blen); x++) { + // need at least 4 the same to be worth it + // must compress tag (if it occurs) + if ((bp[x] == bp[x+1]) && (bp[x] == bp[x+2]) && (bp[x] == bp[x+3]) || (bp[x] == ctag)) { + for (y = 1; (y < 255) && (bp[x] == bp[x+y]); y++) ; + *cp++ = ctag; // tag + *cp++ = y; // length + *cp++ = bp[x]; // byte + x += y -1; + clen += 3; + } else { + *cp++ = bp[x]; + clen++; + } + } + + // create struct + fprintf(fh, "#include \"images.h\"\n\n"); + fprintf(fh, "const image_t img_%s = { %d, %d, ", name, img.w, img.h); + + if (clen < blen) { // dump compressed? + fprintf(fh, "true, %d, 0x%02X, { // orig:%d, comp:%.2f%%\n\t", + clen, ctag, blen, 100.0-((clen*100.0)/blen)); + for (x = 0; x < clen; x++) + if (x == clen -1) fprintf(fh, "0x%02X\n}};\n", cmp[x]) ; + else fprintf(fh, "0x%02X%s", cmp[x], (!((x+1)%16)) ? ",\n\t" : ", ") ; + + } else { // dump UNcompressed + fprintf(fh, "false, %d, 0, {\n\t", blen); + for (x = 0; x < blen; x++) + if (x == blen -1) fprintf(fh, "0x%02X\n}};\n", buf[x]) ; + else fprintf(fh, "0x%02X%s", buf[x], (!((x+1)%16)) ? ",\n\t" : ", ") ; + } + +bail: + if (fh) fclose(fh) ; + if (buf) free(buf) ; + if (cmp) free(cmp) ; + + return rv; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert.sh b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert.sh new file mode 100644 index 000000000..aaa7977b5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +[ -z $1 ] && { + echo "Specify an image" + echo "gimp -> export -> c source file -> [x] gunit names" + exit 2 +} + +echo $* + +for N in $* ; do + + [ ! -f $N ] && { + echo "!! File missing $N" + continue + } + + # filename (sans extension) + FN=$(basename -- "$N") + EXT="${FN##*.}" + NAME="${FN%.*}" + + OUTDIR=img_/ + mkdir -p ${OUTDIR} + + HDR=${OUTDIR}/images.h + SRC=${OUTDIR}/images.c + + OUT=${OUTDIR}/img_${NAME}.c + + echo -e "\n¦${N}¦ == ¦${NAME}¦ -> ¦${OUT}¦" + + TESTX=test_${NAME} + TESTC=test_${NAME}.c + + # compile name + CONV=${NAME}_ + + # clean up gimp output + sed -e "s/gimp_image/img/g" \ + -e 's/guint8/unsigned char/g' \ + -e 's/width/w/g' \ + -e 's/height/h/g' \ + -e 's/bytes_per_pixel/bpp/g' \ + -e 's/pixel_data/b/g' \ + -e 's/guint/unsigned int/g' \ + $N \ + | grep -v ^/ \ + | grep -v ^$ \ + > ${CONV}.c + + # append conversion code + cat _convert.c >> ${CONV}.c + + # compile & run converter + rm -f ${CONV} + gcc ${CONV}.c -DIMGTEST -o ${CONV} + ./${CONV} ${NAME} ${OUT} + rm -f ${CONV} ${CONV}.c + + # (create &) update header + [[ ! -f ${HDR} ]] && cp _convert_images.h ${HDR} + sed -i "/ img_${NAME};/d" ${HDR} + sed -i "s#//\[TAG\]#//\[TAG\]\nextern const image_t img_${NAME};#" ${HDR} + + # sample FZ code + [[ ! -f images.c ]] && cp _convert_images.c ${SRC} + + # test + ROOT=${PWD} + pushd ${OUTDIR} >/dev/null + sed "s/zzz/${NAME}/" ${ROOT}/_convert_test.c > ${TESTC} + rm -f ${TESTX} + gcc ${TESTC} ${OUT##*/} -DIMGTEST -o ${TESTX} + ./${TESTX} + rm -f ${TESTX} ${TESTC} + popd >/dev/null + +done diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert_images.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert_images.c new file mode 100644 index 000000000..57046e9a3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert_images.c @@ -0,0 +1,141 @@ +#include // GUI (screen/keyboard) API + +#include "images.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +static Canvas* _canvas; +static uint8_t _tlx; +static uint8_t _tly; + +static uint8_t _x; +static uint8_t _y; + +static const image_t* _img; + +static bool _blk; +static Color _set; +static Color _clr; + +//+============================================================================ +static +void _showByteSet (const uint8_t b) +{ + for (uint8_t m = 0x80; m; m >>= 1) { + if (b & m) // plot only SET bits + canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ; + if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ; + } +} + +//+============================================================================ +static +void _showByteClr (const uint8_t b) +{ + for (uint8_t m = 0x80; m; m >>= 1) { + if (!(b & m)) // plot only CLR bits + canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ; + if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ; + } +} + +//+============================================================================ +static +void _showByteAll (const uint8_t b) +{ + for (uint8_t m = 0x80; m; m >>= 1) { + if ((!!(b & m)) ^ _blk) { // Change colour only when required + canvas_set_color(_canvas, ((b & m) ? _set : _clr)); + _blk = !_blk; + } + canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ; + if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ; + } +} + +//+============================================================================ +// available modes are SHOW_SET_BLK - plot image pixels that are SET in BLACK +// SHOW_XOR - same as SET_BLACK +// SHOW_SET_WHT - plot image pixels that are SET in WHITE +// SHOW_CLR_BLK - plot image pixels that are CLEAR in BLACK +// SHOW_CLR_WHT - plot image pixels that are CLEAR in WHITE +// SHOW_ALL - plot all images pixels as they are +// SHOW_ALL_INV - plot all images pixels inverted +// +void show (Canvas* const canvas, const uint8_t tlx, const uint8_t tly, + const image_t* img, const showMode_t mode) +{ + void(*fnShow)(const uint8_t) = NULL; + + const uint8_t* bp = img->data; + + // code size optimisation + switch (mode & SHOW_INV_) { + case SHOW_NRM_: + _set = ColorBlack; + _clr = ColorWhite; + break; + + case SHOW_INV_: + _set = ColorWhite; + _clr = ColorBlack; + break; + + case SHOW_BLK_: + canvas_set_color(canvas, ColorBlack); + break; + + case SHOW_WHT_: + canvas_set_color(canvas, ColorWhite); + break; + + } + switch (mode & SHOW_INV_) { + case SHOW_NRM_: + case SHOW_INV_: + fnShow = _showByteAll; + canvas_set_color(canvas, ColorWhite); + _blk = 0; + break; + + case SHOW_BLK_: + case SHOW_WHT_: + switch (mode & SHOW_ALL_) { + case SHOW_SET_: + fnShow = _showByteSet; + break; + case SHOW_CLR_: + fnShow = _showByteClr; + break; + } + break; + } + furi_check(fnShow); + + // I want nested functions! + _canvas = canvas; + _img = img; + _tlx = tlx; + _tly = tly; + _x = 0; + _y = 0; + + // Compressed + if (img->c) { + for (unsigned int i = 0; i < img->len; i++, bp++) { + // Compressed data? {tag, length, value} + if (*bp == img->tag) { + for (uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]) ; + bp += 3 -1; + i += 3 -1; + + // Uncompressed byte + } else { + fnShow(*bp); + } + } + + // Not compressed + } else { + for (unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp) ; + } +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert_images.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert_images.h new file mode 100644 index 000000000..bfc44568e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert_images.h @@ -0,0 +1,53 @@ +#ifndef IMAGES_H_ +#define IMAGES_H_ + +#include +#include + +//----------------------------------------------------------------------------- ---------------------------------------- +typedef + enum showMode { + // {INV:--:WHT:BLK::--:--:CLR:SET} + SHOW_SET_ = 0x01, + SHOW_CLR_ = 0x02, + SHOW_ALL_ = SHOW_SET_ | SHOW_CLR_, + + SHOW_BLK_ = 0x10, + SHOW_WHT_ = 0x20, + SHOW_NRM_ = 0x00, + SHOW_INV_ = SHOW_BLK_ | SHOW_WHT_, + + SHOW_SET_BLK = SHOW_SET_ | SHOW_BLK_, + SHOW_SET_WHT = SHOW_SET_ | SHOW_WHT_, + + SHOW_CLR_BLK = SHOW_CLR_ | SHOW_BLK_, + SHOW_CLR_WHT = SHOW_CLR_ | SHOW_WHT_, + + SHOW_ALL = SHOW_ALL_ | SHOW_NRM_, + SHOW_ALL_INV = SHOW_ALL_ | SHOW_INV_, + } +showMode_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +typedef + struct image { + uint8_t w; // width + uint8_t h; // height + bool c; // compressed? + uint16_t len; // image data length + uint8_t tag; // rle tag + uint8_t data[]; // image data + } +image_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +//[TAG] + +//----------------------------------------------------------------------------- ---------------------------------------- +#ifndef IMGTEST +# include + void show (Canvas* const canvas, const uint8_t tlx, const uint8_t tly, + const image_t* img, const showMode_t mode) ; +#endif + +#endif //IMAGES_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert_test.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert_test.c new file mode 100644 index 000000000..4bdb531d5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_image_tool/_convert_test.c @@ -0,0 +1,59 @@ +#include +#include + +#include "images.h" + +//----------------------------------------------------------------------------- +// This will be the plot function out of your graphics library +// +#define PLOT(x,y,c) do { \ + printf("%s", (c ? "#" : ".")); \ + if (x == img->w -1) printf("\n") ; \ +}while(0) + +//+============================================================================ +// The pain we endure to avoid code duplication cleanly +// +#define PLOTBYTE(b) do { \ + for (uint8_t m = 0x80; m; m>>=1) { \ + PLOT(x,y, (b & m)); \ + if ( ((++x) == img->w) && !(x = 0) && ((++y) == img->h) ) break ; \ + } \ +}while(0) + +void show (const image_t* img) +{ + // Some variables + const uint8_t* bp = img->data; + unsigned int x = 0; + unsigned int y = 0; + + // Compressed + if (img->c) { + for (unsigned int i = 0; i < img->len; i++, bp++) { + // Compressed data? {tag, length, value} + if (*bp == img->tag) { + for (uint16_t c = 0; c < bp[1]; c++) PLOTBYTE(bp[2]) ; + bp += 3 -1; + i += 3 -1; + + // Uncompressed byte + } else { + PLOTBYTE(*bp); + } + } + + // Not compressed + } else { + for (unsigned int i = 0; i < img->len; i++, bp++) PLOTBYTE(*bp) ; + } +} + +#undef PLOTBYTE + +//+============================================================================ +int main (void) +{ + show(&img_zzz); + return 0; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/CLASSIC.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/CLASSIC.png new file mode 100644 index 000000000..aa5318b33 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/CLASSIC.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/CLASSIC_N.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/CLASSIC_N.png new file mode 100644 index 000000000..24f4ac225 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/CLASSIC_N.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/DEBUG.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/DEBUG.png new file mode 100644 index 000000000..bca35c693 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/DEBUG.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/DUMP.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/DUMP.png new file mode 100644 index 000000000..dc9328aab Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/DUMP.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/Nunchuck_acc.xcf b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/Nunchuck_acc.xcf new file mode 100644 index 000000000..67f70139e Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/Nunchuck_acc.xcf differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/RIP.xcf b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/RIP.xcf new file mode 100644 index 000000000..0058fe9c8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/RIP.xcf differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/Wiring.xcf b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/Wiring.xcf new file mode 100644 index 000000000..aa8078db8 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/Wiring.xcf differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/classic.xcf b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/classic.xcf new file mode 100644 index 000000000..6fd152675 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/classic.xcf differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/csLogo.xcf b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/csLogo.xcf new file mode 100644 index 000000000..f4e33844a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/csLogo.xcf differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/fonts.xcf b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/fonts.xcf new file mode 100644 index 000000000..d05d03fc7 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/fonts.xcf differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/frame.xcf b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/frame.xcf new file mode 100644 index 000000000..31705cf72 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/frame.xcf differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/port.xcf b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/port.xcf new file mode 100644 index 000000000..10fcd2de2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/port.xcf differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/social.xcf b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/social.xcf new file mode 100644 index 000000000..377eaa63b Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/GIMP/social.xcf differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK.png new file mode 100644 index 000000000..bc31ae386 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK_acc.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK_acc.png new file mode 100644 index 000000000..895c85e4c Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK_acc.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK_anal.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK_anal.png new file mode 100644 index 000000000..e821d7ee2 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK_anal.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK_cal.gif b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK_cal.gif new file mode 100644 index 000000000..72d807a54 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK_cal.gif differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK_cal.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK_cal.png new file mode 100644 index 000000000..f9d34bb93 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/NUNCHUCK_cal.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/Nunchucky.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/Nunchucky.png new file mode 100644 index 000000000..3af395da6 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/Nunchucky.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/RIP.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/RIP.png new file mode 100644 index 000000000..0acfe0c00 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/RIP.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/SPLASH.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/SPLASH.png new file mode 100644 index 000000000..a5c3f093a Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/SPLASH.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/WAIT.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/WAIT.png new file mode 100644 index 000000000..776edc3f1 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/WAIT.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/WiiChuck.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/WiiChuck.png new file mode 100644 index 000000000..532ce3096 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/WiiChuck.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/Wiring.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/Wiring.png new file mode 100644 index 000000000..300c07ee4 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/Wiring.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/plug.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/plug.png new file mode 100644 index 000000000..c418f43b1 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/plug.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/social.png b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/social.png new file mode 100644 index 000000000..1d3eddcc5 Binary files /dev/null and b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/_images/social.png differ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/application.fam b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/application.fam new file mode 100644 index 000000000..db351b4e5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/application.fam @@ -0,0 +1,36 @@ +# qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md + +App( + # --- App Info + appid="wii_ec_anal", + name="Wii EC Analyser", + + # --- Entry point + apptype=FlipperAppType.EXTERNAL, + entry_point="wii_ec_anal", + + # --- Interaction + cdefines=["APP_WII_EC_ANAL"], + requires=[ + "gui", + ], + +# conflicts="", +# sdk_headers="", + + # --- Run-time info + stack_size=2 * 1024, + order=20, + + # --- FAP details + sources=["wii_*.c", "gfx/*.c"], + +# fap_weburl="https://github.com/csBlueChip/FlipperZero_plugin_WiiChuck/", +# fap_author="BlueChip", + +# fap_description="Wii Extension Controller Protocol Analyser", +# fap_version=(1,0), + + fap_icon="WiiEC.png", + fap_category="GPIO_Extra", +) diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/bc_logging.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/bc_logging.h new file mode 100644 index 000000000..d9bb48c92 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/bc_logging.h @@ -0,0 +1,70 @@ +#ifndef BC_LOGGING_H_ +#define BC_LOGGING_H_ + +#include +#include "err.h" // appName + +//! WARNING: There is a bug in Furi such that if you crank LOG_LEVEL up to 6=TRACE +//! AND you have menu->settings->system->logLevel = trace +//! THEN this program will cause the FZ to crash when the plugin exits! +#define LOG_LEVEL 4 + +//----------------------------------------------------------------------------- ---------------------------------------- +// The FlipperZero Settings->System menu allows you to set the logging level at RUN-time +// ... LOG_LEVEL lets you limit it at COMPILE-time +// +// FURI logging has 6 levels (numbered 1 thru 6} +// 1. None +// 2. Errors FURI_LOG_E +// 3. Warnings FURI_LOG_W +// 4. Information FURI_LOG_I +// 5. Debug FURI_LOG_D +// 6. Trace FURI_LOG_T +// +// --> furi/core/log.h +// + +// The FlipperZero Settings->System menu allows you to set the logging level at RUN-time +// This lets you limit it at COMPILE-time +#ifndef LOG_LEVEL +# define LOG_LEVEL 6 // default = full logging +#endif + +#if (LOG_LEVEL < 2) +# undef FURI_LOG_E +# define FURI_LOG_E(tag, fmt, ...) +#endif + +#if (LOG_LEVEL < 3) +# undef FURI_LOG_W +# define FURI_LOG_W(tag, fmt, ...) +#endif + +#if (LOG_LEVEL < 4) +# undef FURI_LOG_I +# define FURI_LOG_I(tag, fmt, ...) +#endif + +#if (LOG_LEVEL < 5) +# undef FURI_LOG_D +# define FURI_LOG_D(tag, fmt, ...) +#endif + +#if (LOG_LEVEL < 6) +# undef FURI_LOG_T +# define FURI_LOG_T(tag, fmt, ...) +#endif + +//---------------------------------------------------------- +// Logging helper macros +// +#define ERROR(fmt, ...) FURI_LOG_E(appName, fmt __VA_OPT__(,) __VA_ARGS__) +#define WARN(fmt, ...) FURI_LOG_W(appName, fmt __VA_OPT__(,) __VA_ARGS__) +#define INFO(fmt, ...) FURI_LOG_I(appName, fmt __VA_OPT__(,) __VA_ARGS__) +#define DEBUG(fmt, ...) FURI_LOG_D(appName, fmt __VA_OPT__(,) __VA_ARGS__) +#define TRACE(fmt, ...) FURI_LOG_T(appName, fmt __VA_OPT__(,) __VA_ARGS__) + +#define ENTER TRACE("(+) %s", __func__) +#define LEAVE TRACE("(-) %s", __func__) + +#endif //BC_LOGGING_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/err.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/err.h new file mode 100644 index 000000000..9398a3fb8 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/err.h @@ -0,0 +1,69 @@ +// Avoid circular/nested/mulitple inclusion +#ifndef ERR_H_ +#define ERR_H_ + +//----------------------------------------------------------------------------- ---------------------------------------- +// Application name +// +static const char* const appName = "Wii_i2c"; //$ Name used in log files + +//----------------------------------------------------------------------------- ---------------------------------------- +// Error codes and messages +// + +// You should only ever (need to) edit this list +// ...Watch out for extraneous whitespace after the terminating backslashes +#define FOREACH_ES(esPrial) \ + /* The first line MUST define 'ERR_OK = 0' */ \ + esPrial( 0, ERR_OK , "OK (no error)") \ +\ + esPrial( 1, ERR_MALLOC_QUEUE , "malloc() fail - queue") \ + esPrial( 2, ERR_MALLOC_STATE , "malloc() fail - state") \ + esPrial( 3, ERR_MALLOC_TEXT , "malloc() fail - text") \ + esPrial( 4, ERR_MALLOC_VIEW , "malloc() fail - viewport") \ + esPrial( 5, ERR_NO_MUTEX , "Cannot create mutex") \ + esPrial( 6, ERR_NO_GUI , "Cannot open GUI") \ + esPrial( 7, ERR_NO_TIMER , "Cannot create timer") \ + esPrial( 8, ERR_NO_NOTIFY , "Cannot acquire notifications handle") \ +\ + esPrial(10, ERR_MUTEX_BLOCK , "Mutex block failed") \ + esPrial(11, ERR_MUTEX_RELEASE , "Mutex release failed") \ +\ + esPrial(20, ERR_QUEUE_RTOS , "queue - Undefined RTOS error") \ + esPrial(21, DEBUG_QUEUE_TIMEOUT, "queue - Timeout") \ + esPrial(22, ERR_QUEUE_RESOURCE , "queue - Resource not available") \ + esPrial(23, ERR_QUEUE_BADPRM , "queue - Bad parameter") \ + esPrial(24, ERR_QUEUE_NOMEM , "queue - Out of memory") \ + esPrial(25, ERR_QUEUE_ISR , "queue - Banned in ISR") \ + esPrial(26, ERR_QUEUE_UNK , "queue - Unknown") \ +\ + esPrial(30, WARN_SCAN_START , "Scan - Already started") \ + esPrial(31, WARN_SCAN_STOP , "Scan - Already stopped") \ + esPrial(32, ERR_TIMER_START , "Scan - Cannot start timer") \ + esPrial(33, ERR_TIMER_STOP , "Scan - Cannot stop timer") \ +//[EOT] + +// Declare list extraction macros +#define ES_ENUM(num, ename, string) ename = num, +#define ES_STRING(num, ename, string) string"\r\n", + +// Build the enum +typedef + enum err { FOREACH_ES(ES_ENUM) } +err_t ; + +// You need to '#define ERR_C_' in precisely ONE source file +#ifdef ERR_C_ + // Build the string list + const char* const wii_errs[] = { FOREACH_ES(ES_STRING) }; +#else + // Give access to string list + extern const char* const wii_errs[]; +#endif + +// This is a header file, clean up +#undef ES_ENUM +#undef ES_STRING +#undef FOREACH_ES + +#endif // ERR_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/images.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/images.c new file mode 100644 index 000000000..57046e9a3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/images.c @@ -0,0 +1,141 @@ +#include // GUI (screen/keyboard) API + +#include "images.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +static Canvas* _canvas; +static uint8_t _tlx; +static uint8_t _tly; + +static uint8_t _x; +static uint8_t _y; + +static const image_t* _img; + +static bool _blk; +static Color _set; +static Color _clr; + +//+============================================================================ +static +void _showByteSet (const uint8_t b) +{ + for (uint8_t m = 0x80; m; m >>= 1) { + if (b & m) // plot only SET bits + canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ; + if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ; + } +} + +//+============================================================================ +static +void _showByteClr (const uint8_t b) +{ + for (uint8_t m = 0x80; m; m >>= 1) { + if (!(b & m)) // plot only CLR bits + canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ; + if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ; + } +} + +//+============================================================================ +static +void _showByteAll (const uint8_t b) +{ + for (uint8_t m = 0x80; m; m >>= 1) { + if ((!!(b & m)) ^ _blk) { // Change colour only when required + canvas_set_color(_canvas, ((b & m) ? _set : _clr)); + _blk = !_blk; + } + canvas_draw_dot(_canvas, (_tlx +_x), (_tly +_y)) ; + if ( ((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h) ) break ; + } +} + +//+============================================================================ +// available modes are SHOW_SET_BLK - plot image pixels that are SET in BLACK +// SHOW_XOR - same as SET_BLACK +// SHOW_SET_WHT - plot image pixels that are SET in WHITE +// SHOW_CLR_BLK - plot image pixels that are CLEAR in BLACK +// SHOW_CLR_WHT - plot image pixels that are CLEAR in WHITE +// SHOW_ALL - plot all images pixels as they are +// SHOW_ALL_INV - plot all images pixels inverted +// +void show (Canvas* const canvas, const uint8_t tlx, const uint8_t tly, + const image_t* img, const showMode_t mode) +{ + void(*fnShow)(const uint8_t) = NULL; + + const uint8_t* bp = img->data; + + // code size optimisation + switch (mode & SHOW_INV_) { + case SHOW_NRM_: + _set = ColorBlack; + _clr = ColorWhite; + break; + + case SHOW_INV_: + _set = ColorWhite; + _clr = ColorBlack; + break; + + case SHOW_BLK_: + canvas_set_color(canvas, ColorBlack); + break; + + case SHOW_WHT_: + canvas_set_color(canvas, ColorWhite); + break; + + } + switch (mode & SHOW_INV_) { + case SHOW_NRM_: + case SHOW_INV_: + fnShow = _showByteAll; + canvas_set_color(canvas, ColorWhite); + _blk = 0; + break; + + case SHOW_BLK_: + case SHOW_WHT_: + switch (mode & SHOW_ALL_) { + case SHOW_SET_: + fnShow = _showByteSet; + break; + case SHOW_CLR_: + fnShow = _showByteClr; + break; + } + break; + } + furi_check(fnShow); + + // I want nested functions! + _canvas = canvas; + _img = img; + _tlx = tlx; + _tly = tly; + _x = 0; + _y = 0; + + // Compressed + if (img->c) { + for (unsigned int i = 0; i < img->len; i++, bp++) { + // Compressed data? {tag, length, value} + if (*bp == img->tag) { + for (uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]) ; + bp += 3 -1; + i += 3 -1; + + // Uncompressed byte + } else { + fnShow(*bp); + } + } + + // Not compressed + } else { + for (unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp) ; + } +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/images.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/images.h new file mode 100644 index 000000000..87f2b89b7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/images.h @@ -0,0 +1,134 @@ +#ifndef IMAGES_H_ +#define IMAGES_H_ + +#include +#include + +//----------------------------------------------------------------------------- ---------------------------------------- +typedef + enum showMode { + // {INV:--:WHT:BLK::--:--:CLR:SET} + SHOW_SET_ = 0x01, + SHOW_CLR_ = 0x02, + SHOW_ALL_ = SHOW_SET_ | SHOW_CLR_, + + SHOW_BLK_ = 0x10, + SHOW_WHT_ = 0x20, + SHOW_NRM_ = 0x00, + SHOW_INV_ = SHOW_BLK_ | SHOW_WHT_, + + SHOW_SET_BLK = SHOW_SET_ | SHOW_BLK_, + SHOW_SET_WHT = SHOW_SET_ | SHOW_WHT_, + + SHOW_CLR_BLK = SHOW_CLR_ | SHOW_BLK_, + SHOW_CLR_WHT = SHOW_CLR_ | SHOW_WHT_, + + SHOW_ALL = SHOW_ALL_ | SHOW_NRM_, + SHOW_ALL_INV = SHOW_ALL_ | SHOW_INV_, + } +showMode_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +typedef + struct image { + uint8_t w; // width + uint8_t h; // height + bool c; // compressed? + uint16_t len; // image data length + uint8_t tag; // rle tag + uint8_t data[]; // image data + } +image_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +//[TAG] +extern const image_t img_csLogo_Small; +extern const image_t img_3x5_v; +extern const image_t img_3x5_9; +extern const image_t img_3x5_8; +extern const image_t img_3x5_7; +extern const image_t img_3x5_6; +extern const image_t img_3x5_5; +extern const image_t img_3x5_4; +extern const image_t img_3x5_3; +extern const image_t img_3x5_2; +extern const image_t img_3x5_1; +extern const image_t img_3x5_0; +extern const image_t img_key_Ui; +extern const image_t img_key_OKi; +extern const image_t img_RIP; +extern const image_t img_cc_trg_R4; +extern const image_t img_cc_trg_R3; +extern const image_t img_cc_trg_R2; +extern const image_t img_cc_trg_R1; +extern const image_t img_cc_trg_L4; +extern const image_t img_cc_trg_L3; +extern const image_t img_cc_trg_L2; +extern const image_t img_cc_trg_L1; +extern const image_t img_cc_Joy; +extern const image_t img_cc_Main; +extern const image_t img_cc_Cable; +extern const image_t img_key_Back; +extern const image_t img_key_OK; +extern const image_t img_6x8_Z; +extern const image_t img_6x8_Y; +extern const image_t img_6x8_X; +extern const image_t img_key_U; +extern const image_t img_key_D; +extern const image_t img_csLogo_FULL; +extern const image_t img_6x8_7; +extern const image_t img_key_R; +extern const image_t img_key_L; +extern const image_t img_5x7_7; +extern const image_t img_5x7_F; +extern const image_t img_5x7_E; +extern const image_t img_5x7_D; +extern const image_t img_5x7_C; +extern const image_t img_5x7_B; +extern const image_t img_5x7_A; +extern const image_t img_5x7_9; +extern const image_t img_5x7_8; +extern const image_t img_5x7_6; +extern const image_t img_5x7_5; +extern const image_t img_5x7_4; +extern const image_t img_5x7_3; +extern const image_t img_5x7_2; +extern const image_t img_5x7_1; +extern const image_t img_5x7_0; +extern const image_t img_6x8_v; +extern const image_t img_6x8_n; +extern const image_t img_6x8_G; +extern const image_t img_6x8_F; +extern const image_t img_6x8_E; +extern const image_t img_6x8_d; +extern const image_t img_6x8_C; +extern const image_t img_6x8_B; +extern const image_t img_6x8_A; +extern const image_t img_6x8_9; +extern const image_t img_6x8_8; +extern const image_t img_6x8_6; +extern const image_t img_6x8_5; +extern const image_t img_6x8_4; +extern const image_t img_6x8_3; +extern const image_t img_6x8_2; +extern const image_t img_6x8_1; +extern const image_t img_6x8_0; +extern const image_t img_ecp_SDA; +extern const image_t img_ecp_SCL; +extern const image_t img_ecp_port; +extern const image_t img_cc_pad_UD1; +extern const image_t img_cc_pad_LR1; +extern const image_t img_cc_btn_Y1; +extern const image_t img_cc_btn_X1; +extern const image_t img_cc_btn_B1; +extern const image_t img_cc_btn_A1; +extern const image_t img_6x8_D; + +//----------------------------------------------------------------------------- ---------------------------------------- +#ifndef IMGTEST +# include + void show (Canvas* const canvas, const uint8_t tlx, const uint8_t tly, + const image_t* img, const showMode_t mode) ; +#endif + +#endif //IMAGES_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_0.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_0.c new file mode 100644 index 000000000..975d98d35 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_0.c @@ -0,0 +1,11 @@ +// ###### +// ##..## +// ##..## +// ##..## +// ###### + +#include "images.h" + +const image_t img_3x5_0 = { 3, 5, false, 2, 0, { + 0xF6, 0xDE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_1.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_1.c new file mode 100644 index 000000000..0d9dc3fe4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_1.c @@ -0,0 +1,11 @@ +// ####.. +// ..##.. +// ..##.. +// ..##.. +// ###### + +#include "images.h" + +const image_t img_3x5_1 = { 3, 5, false, 2, 0, { + 0xC9, 0x2E +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_2.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_2.c new file mode 100644 index 000000000..d98bf4e93 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_2.c @@ -0,0 +1,11 @@ +// ###### +// ....## +// ###### +// ##.... +// ###### + +#include "images.h" + +const image_t img_3x5_2 = { 3, 5, false, 2, 0, { + 0xE7, 0xCE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_3.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_3.c new file mode 100644 index 000000000..8d08ed1b6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_3.c @@ -0,0 +1,11 @@ +// ###### +// ....## +// ..#### +// ....## +// ###### + +#include "images.h" + +const image_t img_3x5_3 = { 3, 5, false, 2, 0, { + 0xE5, 0x9E +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_4.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_4.c new file mode 100644 index 000000000..795e9b76f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_4.c @@ -0,0 +1,11 @@ +// ##.... +// ##..## +// ###### +// ....## +// ....## + +#include "images.h" + +const image_t img_3x5_4 = { 3, 5, false, 2, 0, { + 0x97, 0x92 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_5.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_5.c new file mode 100644 index 000000000..377853507 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_5.c @@ -0,0 +1,11 @@ +// ###### +// ##.... +// ###### +// ....## +// ###### + +#include "images.h" + +const image_t img_3x5_5 = { 3, 5, false, 2, 0, { + 0xF3, 0x9E +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_6.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_6.c new file mode 100644 index 000000000..d3af64071 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_6.c @@ -0,0 +1,11 @@ +// ####.. +// ##.... +// ###### +// ##..## +// ###### + +#include "images.h" + +const image_t img_3x5_6 = { 3, 5, false, 2, 0, { + 0xD3, 0xDE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_7.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_7.c new file mode 100644 index 000000000..2c3b1e0b9 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_7.c @@ -0,0 +1,11 @@ +// ###### +// ....## +// ..##.. +// ..##.. +// ..##.. + +#include "images.h" + +const image_t img_3x5_7 = { 3, 5, false, 2, 0, { + 0xE5, 0x24 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_8.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_8.c new file mode 100644 index 000000000..5cb6d3354 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_8.c @@ -0,0 +1,11 @@ +// ###### +// ##..## +// ###### +// ##..## +// ###### + +#include "images.h" + +const image_t img_3x5_8 = { 3, 5, false, 2, 0, { + 0xF7, 0xDE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_9.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_9.c new file mode 100644 index 000000000..ee5e82b87 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_9.c @@ -0,0 +1,11 @@ +// ###### +// ##..## +// ###### +// ....## +// ..#### + +#include "images.h" + +const image_t img_3x5_9 = { 3, 5, false, 2, 0, { + 0xF7, 0x96 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_v.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_v.c new file mode 100644 index 000000000..dcf3f631d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_3x5_v.c @@ -0,0 +1,11 @@ +// ...... +// ...... +// ##..## +// ##..## +// ..##.. + +#include "images.h" + +const image_t img_3x5_v = { 3, 5, false, 2, 0, { + 0x02, 0xD4 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_0.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_0.c new file mode 100644 index 000000000..c59852f19 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_0.c @@ -0,0 +1,13 @@ +// ..######.. +// ##......## +// ##....#### +// ##..##..## +// ####....## +// ##......## +// ..######.. + +#include "images.h" + +const image_t img_5x7_0 = { 5, 7, false, 5, 0, { + 0x74, 0x67, 0x5C, 0xC5, 0xC0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_1.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_1.c new file mode 100644 index 000000000..4bd08f89c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_1.c @@ -0,0 +1,13 @@ +// ..####.... +// ##..##.... +// ....##.... +// ....##.... +// ....##.... +// ....##.... +// ########## + +#include "images.h" + +const image_t img_5x7_1 = { 5, 7, false, 5, 0, { + 0x65, 0x08, 0x42, 0x13, 0xE0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_2.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_2.c new file mode 100644 index 000000000..1270393f7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_2.c @@ -0,0 +1,13 @@ +// ..######.. +// ##......## +// ........## +// ......##.. +// ....##.... +// ..##...... +// ########## + +#include "images.h" + +const image_t img_5x7_2 = { 5, 7, false, 5, 0, { + 0x74, 0x42, 0x22, 0x23, 0xE0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_3.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_3.c new file mode 100644 index 000000000..e26bac523 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_3.c @@ -0,0 +1,13 @@ +// ..######.. +// ##......## +// ........## +// ....####.. +// ........## +// ##......## +// ..######.. + +#include "images.h" + +const image_t img_5x7_3 = { 5, 7, false, 5, 0, { + 0x74, 0x42, 0x60, 0xC5, 0xC0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_4.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_4.c new file mode 100644 index 000000000..e0dc5687f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_4.c @@ -0,0 +1,13 @@ +// ##........ +// ##........ +// ##....##.. +// ##....##.. +// ########## +// ......##.. +// ......##.. + +#include "images.h" + +const image_t img_5x7_4 = { 5, 7, false, 5, 0, { + 0x84, 0x25, 0x2F, 0x88, 0x40 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_5.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_5.c new file mode 100644 index 000000000..81747376f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_5.c @@ -0,0 +1,13 @@ +// ########## +// ##........ +// ##........ +// ########.. +// ........## +// ........## +// ########.. + +#include "images.h" + +const image_t img_5x7_5 = { 5, 7, false, 5, 0, { + 0xFC, 0x21, 0xE0, 0x87, 0xC0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_6.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_6.c new file mode 100644 index 000000000..455c874dc --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_6.c @@ -0,0 +1,13 @@ +// ..######.. +// ##........ +// ##........ +// ########.. +// ##......## +// ##......## +// ..######.. + +#include "images.h" + +const image_t img_5x7_6 = { 5, 7, false, 5, 0, { + 0x74, 0x21, 0xE8, 0xC5, 0xC0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_7.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_7.c new file mode 100644 index 000000000..73e813a21 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_7.c @@ -0,0 +1,13 @@ +// ########## +// ........## +// ......##.. +// ......##.. +// ....##.... +// ....##.... +// ....##.... + +#include "images.h" + +const image_t img_5x7_7 = { 5, 7, false, 5, 0, { + 0xF8, 0x44, 0x22, 0x10, 0x80 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_8.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_8.c new file mode 100644 index 000000000..0f04a48bf --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_8.c @@ -0,0 +1,13 @@ +// ..######.. +// ##......## +// ##......## +// ..######.. +// ##......## +// ##......## +// ..######.. + +#include "images.h" + +const image_t img_5x7_8 = { 5, 7, false, 5, 0, { + 0x74, 0x62, 0xE8, 0xC5, 0xC0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_9.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_9.c new file mode 100644 index 000000000..2b1e978c6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_9.c @@ -0,0 +1,13 @@ +// ..######.. +// ##......## +// ##......## +// ..######## +// ........## +// ........## +// ..######.. + +#include "images.h" + +const image_t img_5x7_9 = { 5, 7, false, 5, 0, { + 0x74, 0x62, 0xF0, 0x85, 0xC0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_A.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_A.c new file mode 100644 index 000000000..a6b049f01 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_A.c @@ -0,0 +1,13 @@ +// ..######.. +// ##......## +// ##......## +// ########## +// ##......## +// ##......## +// ##......## + +#include "images.h" + +const image_t img_5x7_A = { 5, 7, false, 5, 0, { + 0x74, 0x63, 0xF8, 0xC6, 0x20 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_B.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_B.c new file mode 100644 index 000000000..06b36599c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_B.c @@ -0,0 +1,13 @@ +// ########.. +// ##......## +// ##......## +// ##..####.. +// ##......## +// ##......## +// ########.. + +#include "images.h" + +const image_t img_5x7_B = { 5, 7, false, 5, 0, { + 0xF4, 0x63, 0x68, 0xC7, 0xC0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_C.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_C.c new file mode 100644 index 000000000..c058d09a9 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_C.c @@ -0,0 +1,13 @@ +// ..######.. +// ##......## +// ##........ +// ##........ +// ##........ +// ##......## +// ..######.. + +#include "images.h" + +const image_t img_5x7_C = { 5, 7, false, 5, 0, { + 0x74, 0x61, 0x08, 0x45, 0xC0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_D.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_D.c new file mode 100644 index 000000000..3425e3648 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_D.c @@ -0,0 +1,13 @@ +// ..######.. +// ##..##..## +// ....##..## +// ....##..## +// ....##..## +// ##..##..## +// ..######.. + +#include "images.h" + +const image_t img_5x7_D = { 5, 7, false, 5, 0, { + 0x75, 0x4A, 0x52, 0xD5, 0xC0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_E.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_E.c new file mode 100644 index 000000000..c7bbc301a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_E.c @@ -0,0 +1,13 @@ +// ########## +// ##........ +// ##........ +// ######.... +// ##........ +// ##........ +// ########## + +#include "images.h" + +const image_t img_5x7_E = { 5, 7, false, 5, 0, { + 0xFC, 0x21, 0xC8, 0x43, 0xE0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_F.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_F.c new file mode 100644 index 000000000..440c37eae --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_5x7_F.c @@ -0,0 +1,13 @@ +// ########## +// ##........ +// ##........ +// ######.... +// ##........ +// ##........ +// ##........ + +#include "images.h" + +const image_t img_5x7_F = { 5, 7, false, 5, 0, { + 0xFC, 0x21, 0xC8, 0x42, 0x00 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_0.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_0.c new file mode 100644 index 000000000..b8b4c7d9a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_0.c @@ -0,0 +1,14 @@ +// ..########.. +// ############ +// ####....#### +// ####....#### +// ####....#### +// ####....#### +// ############ +// ..########.. + +#include "images.h" + +const image_t img_6x8_0 = { 6, 8, false, 6, 0, { + 0x7B, 0xFC, 0xF3, 0xCF, 0x3F, 0xDE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_1.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_1.c new file mode 100644 index 000000000..91e2b2cfa --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_1.c @@ -0,0 +1,14 @@ +// ..######.... +// ########.... +// ....####.... +// ....####.... +// ....####.... +// ....####.... +// ############ +// ############ + +#include "images.h" + +const image_t img_6x8_1 = { 6, 8, false, 6, 0, { + 0x73, 0xC3, 0x0C, 0x30, 0xCF, 0xFF +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_2.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_2.c new file mode 100644 index 000000000..7d24c64d6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_2.c @@ -0,0 +1,14 @@ +// ..########.. +// ############ +// ........#### +// ......###### +// ....####.... +// ..####...... +// ############ +// ############ + +#include "images.h" + +const image_t img_6x8_2 = { 6, 8, false, 6, 0, { + 0x7B, 0xF0, 0xC7, 0x31, 0x8F, 0xFF +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_3.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_3.c new file mode 100644 index 000000000..3a8f9f211 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_3.c @@ -0,0 +1,14 @@ +// ..########.. +// ############ +// ........#### +// ....######## +// ....######## +// ........#### +// ############ +// ..########.. + +#include "images.h" + +const image_t img_6x8_3 = { 6, 8, false, 6, 0, { + 0x7B, 0xF0, 0xCF, 0x3C, 0x3F, 0xDE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_4.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_4.c new file mode 100644 index 000000000..c5ae9efef --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_4.c @@ -0,0 +1,14 @@ +// ####........ +// ####........ +// ####..####.. +// ####..####.. +// ############ +// ############ +// ......####.. +// ......####.. + +#include "images.h" + +const image_t img_6x8_4 = { 6, 8, false, 6, 0, { + 0xC3, 0x0D, 0xB6, 0xFF, 0xF1, 0x86 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_5.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_5.c new file mode 100644 index 000000000..787e39ea6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_5.c @@ -0,0 +1,14 @@ +// ############ +// ############ +// ####........ +// ##########.. +// ############ +// ........#### +// ############ +// ##########.. + +#include "images.h" + +const image_t img_6x8_5 = { 6, 8, false, 6, 0, { + 0xFF, 0xFC, 0x3E, 0xFC, 0x3F, 0xFE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_6.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_6.c new file mode 100644 index 000000000..8f07f1bfc --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_6.c @@ -0,0 +1,14 @@ +// ..########.. +// ##########.. +// ####........ +// ##########.. +// ############ +// ####....#### +// ############ +// ..########.. + +#include "images.h" + +const image_t img_6x8_6 = { 6, 8, false, 6, 0, { + 0x7B, 0xEC, 0x3E, 0xFF, 0x3F, 0xDE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_7.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_7.c new file mode 100644 index 000000000..cad50c65d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_7.c @@ -0,0 +1,14 @@ +// ############ +// ############ +// ........#### +// ......####.. +// ......####.. +// ....####.... +// ....####.... +// ....####.... + +#include "images.h" + +const image_t img_6x8_7 = { 6, 8, false, 6, 0, { + 0xFF, 0xF0, 0xC6, 0x18, 0xC3, 0x0C +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_8.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_8.c new file mode 100644 index 000000000..a38b2110d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_8.c @@ -0,0 +1,14 @@ +// ..########.. +// ############ +// ####....#### +// ..########.. +// ############ +// ####....#### +// ############ +// ..########.. + +#include "images.h" + +const image_t img_6x8_8 = { 6, 8, false, 6, 0, { + 0x7B, 0xFC, 0xDE, 0xFF, 0x3F, 0xDE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_9.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_9.c new file mode 100644 index 000000000..b740c7f90 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_9.c @@ -0,0 +1,14 @@ +// ..########.. +// ############ +// ####....#### +// ############ +// ..########## +// ........#### +// ..########## +// ..########.. + +#include "images.h" + +const image_t img_6x8_9 = { 6, 8, false, 6, 0, { + 0x7B, 0xFC, 0xFF, 0x7C, 0x37, 0xDE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_A.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_A.c new file mode 100644 index 000000000..fa3aed598 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_A.c @@ -0,0 +1,14 @@ +// ..########.. +// ############ +// ####....#### +// ####....#### +// ############ +// ############ +// ####....#### +// ####....#### + +#include "images.h" + +const image_t img_6x8_A = { 6, 8, false, 6, 0, { + 0x7B, 0xFC, 0xF3, 0xFF, 0xFC, 0xF3 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_B.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_B.c new file mode 100644 index 000000000..14c1e28c6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_B.c @@ -0,0 +1,14 @@ +// ##########.. +// ############ +// ####....#### +// ##########.. +// ##########.. +// ####....#### +// ############ +// ##########.. + +#include "images.h" + +const image_t img_6x8_B = { 6, 8, false, 6, 0, { + 0xFB, 0xFC, 0xFE, 0xFB, 0x3F, 0xFE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_C.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_C.c new file mode 100644 index 000000000..6d8f7aa32 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_C.c @@ -0,0 +1,14 @@ +// ..########## +// ############ +// ####........ +// ####........ +// ####........ +// ####........ +// ############ +// ..########## + +#include "images.h" + +const image_t img_6x8_C = { 6, 8, false, 6, 0, { + 0x7F, 0xFC, 0x30, 0xC3, 0x0F, 0xDF +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_D.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_D.c new file mode 100644 index 000000000..474e4a235 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_D.c @@ -0,0 +1,14 @@ +// ##########.. +// ############ +// ..####..#### +// ..####..#### +// ..####..#### +// ..####..#### +// ############ +// ##########.. + +#include "images.h" + +const image_t img_6x8_D = { 6, 8, false, 6, 0, { + 0xFB, 0xF6, 0xDB, 0x6D, 0xBF, 0xFE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_E.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_E.c new file mode 100644 index 000000000..00f2cb559 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_E.c @@ -0,0 +1,14 @@ +// ############ +// ############ +// ####........ +// ########.... +// ########.... +// ####........ +// ############ +// ############ + +#include "images.h" + +const image_t img_6x8_E = { 6, 8, false, 6, 0, { + 0xFF, 0xFC, 0x3C, 0xF3, 0x0F, 0xFF +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_F.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_F.c new file mode 100644 index 000000000..8958a0419 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_F.c @@ -0,0 +1,14 @@ +// ############ +// ############ +// ####........ +// ########.... +// ########.... +// ####........ +// ####........ +// ####........ + +#include "images.h" + +const image_t img_6x8_F = { 6, 8, false, 6, 0, { + 0xFF, 0xFC, 0x3C, 0xF3, 0x0C, 0x30 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_G.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_G.c new file mode 100644 index 000000000..f5e8f03f4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_G.c @@ -0,0 +1,14 @@ +// ..########## +// ############ +// ####........ +// ####........ +// ####..###### +// ####....#### +// ############ +// ..########## + +#include "images.h" + +const image_t img_6x8_G = { 6, 8, false, 6, 0, { + 0x7F, 0xFC, 0x30, 0xDF, 0x3F, 0xDF +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_X.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_X.c new file mode 100644 index 000000000..7b162baf3 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_X.c @@ -0,0 +1,14 @@ +// ####....#### +// ####....#### +// ..####..##.. +// ....######.. +// ..######.... +// ..##..####.. +// ####....#### +// ####....#### + +#include "images.h" + +const image_t img_6x8_X = { 6, 8, false, 6, 0, { + 0xCF, 0x36, 0x8E, 0x71, 0x6C, 0xF3 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_Y.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_Y.c new file mode 100644 index 000000000..b39392948 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_Y.c @@ -0,0 +1,14 @@ +// ####....#### +// ####....#### +// ####....#### +// ####....#### +// ..########.. +// ....####.... +// ....####.... +// ....####.... + +#include "images.h" + +const image_t img_6x8_Y = { 6, 8, false, 6, 0, { + 0xCF, 0x3C, 0xF3, 0x78, 0xC3, 0x0C +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_Z.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_Z.c new file mode 100644 index 000000000..9904d08b4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_Z.c @@ -0,0 +1,14 @@ +// ############ +// ############ +// ........#### +// ......####.. +// ....####.... +// ..####...... +// ############ +// ############ + +#include "images.h" + +const image_t img_6x8_Z = { 6, 8, false, 6, 0, { + 0xFF, 0xF0, 0xC6, 0x31, 0x8F, 0xFF +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_d_.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_d_.c new file mode 100644 index 000000000..2a00713fd --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_d_.c @@ -0,0 +1,14 @@ +// ........#### +// ........#### +// ........#### +// ..########## +// ############ +// ####....#### +// ############ +// ..########## + +#include "images.h" + +const image_t img_6x8_d = { 6, 8, false, 6, 0, { + 0x0C, 0x30, 0xDF, 0xFF, 0x3F, 0xDF +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_n_.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_n_.c new file mode 100644 index 000000000..086bdd2de --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_n_.c @@ -0,0 +1,14 @@ +// ............ +// ............ +// ..########.. +// ############ +// ####....#### +// ####....#### +// ####....#### +// ####....#### + +#include "images.h" + +const image_t img_6x8_n = { 6, 8, false, 6, 0, { + 0x00, 0x07, 0xBF, 0xCF, 0x3C, 0xF3 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_v_.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_v_.c new file mode 100644 index 000000000..c897aadff --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_6x8_v_.c @@ -0,0 +1,14 @@ +// ............ +// ............ +// ##........## +// ####....#### +// ####....#### +// ############ +// ..########.. +// ....####.... + +#include "images.h" + +const image_t img_6x8_v = { 6, 8, false, 6, 0, { + 0x00, 0x08, 0x73, 0xCF, 0xF7, 0x8C +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_RIP.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_RIP.c new file mode 100644 index 000000000..55cb7bfc2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_RIP.c @@ -0,0 +1,122 @@ +// ################################################################################################################################################################################################################################################################ +// ################################################################################################################################################################################################################################################################ +// ####........................................................................................................................................................................................................................................................#### +// ####..##..##........................................................................................................................................................................................................................................##..##..#### +// ####....##....................##############..........########........##################........................##############......##..........##......##############......##############......##############......##############....................##....#### +// ####..##..##..................##############............######..........################........................##############....######......######....##############......##############......##############......##############..................##..##..#### +// ####....................................######..........##..##......................######....................####........######..######......######..####........######..######......######..............######..######......######........................#### +// ####........................######......######..........##..##..........######......##..##....................######......##..##..##..##......##..##..######......######..######......######..######......######..######......##..##........................#### +// ####........................######......##..##..........##..##..........######......##..##....................##..##......######..##..##......##..##..######......##..##..##..##......##..##..######......##..##..##..##......##..##........................#### +// ####........................##..##......##..##..........##..##..........##..##......##..##....................##..##..............##..##......##..##..##..##......##..##..##..##......##..##..##..##......##..##..##..##......##..##........................#### +// ####........................##..##......##..##..........##..##..........##..##......##..##....................##..##..............##..##......##..##..##..##......##..##..##..##......##..##..##..##......##..##..##..##......######........................#### +// ####........................##..##......######..........##..##..........##..##......######....................##..##..............##..##......##..##..##..##......######..##..##..##..##..##..##..##......######..##..##....................................#### +// ####........................##..##########..............##..##..........##..##########........................##..##..............######......######..##..##########......##..##......##..##..##..##########......##..##....................................#### +// ####........................##..##########..............##..##..........##..##########........................##..##................##############....##..##########......##..##......##..##..##..##########......##..##..########..........................#### +// ####........................##..##....######............##..##..........##..##................................##..##..................####..####......##..##......######..##..##..##..##..##..##..##....######....##..##..##########........................#### +// ####........................##..##....##..##............##..##..........##..##................................##..##....................##..##........##..##......##..##..##..##......##..##..##..##....##..##....##..##......##..##........................#### +// ####........................##..##....##..##............##..##..........##..##................................##..##....................##..##........##..##......##..##..##..##......##..##..##..##....##..##....##..##......##..##........................#### +// ####........................##..##....##..##............##..##..........##..##................................######......######........##..##........##..##......##..##..##..##......##..##..##..##....##..##....######......##..##........................#### +// ####........................##..##....##..##............##..##..........##..##................................######......##..##........##..##........######......######..######......######..##..##....##..##....######......##..##........................#### +// ####........................##..##....##..##............##..##..........##..##................................####........######........######........######......######..######......######..##..##....##..##....####........######........................#### +// ####........................######....######....####....######..####....######..####............................##############..........######..........##############......##############....######....######......##############..........................#### +// ####........................######......######..####..########..####..########..####............................##############........##########........##############......##############....######......######....##############..........................#### +// ####........................................................................................................................................................................................................................................................#### +// ####........................................................................................................................................................................................................................................................#### +// ####..........................................................................................................................................................................................####......####..............##############....................#### +// ####......................................................................................................................................##................................................##....##..##....##........####..............####................#### +// ####..........######..##..##..######......######..##..##..######..######..######..####..####..######......##......##..######..##....##..##..######......######..######......................####..##..####..##......##......................##..............#### +// ####............##....##..##..##..........##......##..##..##........##....##......##..##..##..##..........##......##..##..##..####..##........##........##..##..##............................####......####......##......##########..........##............#### +// ####............##....######..####........######....##....######....##....####....##..##..##..######......##..##..##..##..##..##..####........##........####....####..............................##..##........##......##..........##........##............#### +// ####............##....##..##..##..............##....##........##....##....##......##......##......##......##..##..##..##..##..##....##........##........##..##..##..................................##..........##....##..............##......##............#### +// ####............##....##..##..######......######....##....######....##....######..##......##..######......####..####..######..##....##........##........######..######............................######........##....##......####....##......##............#### +// ####............................................................................................................................................................................................##......##......##....##....##....##..##......##............#### +// ####..........................................................................................................................................................................................##........##......##....##....##..##....##......##............#### +// ####..........................................................................................................................................................................................##........##......##....##....##........##......##............#### +// ####..........######..##..##..######......######..######..####..####..######......##......##..######..######..##..##..######..##..##..######......##..##..######..##..##........................##........######......##......########......##..............#### +// ####............##....##..##..##..........##......##..##..##..##..##..##..........##......##....##......##....##..##..##..##..##..##....##........##..##..##..##..##..##........................##..............##......##..................####............#### +// ####............##....######..####........######..######..##..##..##..####........##..##..##....##......##....######..##..##..##..##....##..........##....##..##..##..##..........................##..............##########..............##....##..........#### +// ####............##....##..##..##..............##..##..##..##......##..##..........##..##..##....##......##....##..##..##..##..##..##....##..........##....##..##..##..##..........................##......................################........##........#### +// ####............##....##..##..######......######..##..##..##......##..######......####..####..######....##....##..##..######..######....##..........##....######..######............................####..........................................##........#### +// ####....................................................................................................................................................................................................##########################################..........#### +// ####........................................................................................................................................................................................................................................................#### +// ####........................................................................................................................................................................................................................................................#### +// ####......................................##########......##############......##############......##############............................##############......##############......##############......##############......................................#### +// ####......................................##########......##############......##############......##############............................##############......##############......##############......##############......................................#### +// ####......................................##..##..##....######......######..######......######..######......######........................######......######..######......######..######......######..######......######....................................#### +// ####..........................................##..##....######......######..##..##......######..######......######........................##..##......##..##..######......######..##..##......##..##..##..##......##..##....................................#### +// ####..........................................##..##....##..##......##..##..######......##..##..##..##......##..##........................######......##..##..##..##......##..##..######......##..##..######......##..##....................................#### +// ####..........................................##..##....##..##......##..##..............##..##..##..##......##..##....................................##..##..##..##......##..##..............##..##..............##..##....................................#### +// ####..........................................##..##....##..##......##..##..............##..##..##..##....####..##....................................##..##..##..##....####..##..............##..##..............##..##....................................#### +// ####..........................................##..##....######......##..##..............######..##..##....####..##....................................######..##..##....####..##..............######..............######....................................#### +// ####..........................................######........##########..##..............######..##..##..##..##..##....################........############....##..##..##..##..##......############........############......................................#### +// ####..........................................######........##########..##..........######......##..##..##..##..##....##............##......############......##..##..##..##..##....############........############........................................#### +// ####..........................................##..##................##..##..........######......##..####....##..##....################....######..............##..####....##..##..######..............######................................................#### +// ####..........................................##..##................##..##..........##..##......##..####....##..##........................##..##..............##..####....##..##..##..##..............##..##................................................#### +// ####..........................................##..##................##..##..........##..##......##..##......##..##........................##..##..............##..##......##..##..##..##..............##..##................................................#### +// ####..........................................##..##................##..##..........##..##......##..##......##..##........................##..##..............##..##......##..##..##..##..............##..##................................................#### +// ####..........................................##..##................######..........##..##......######......######........................##..##..............######......######..##..##..............##..##................................................#### +// ####..........................................##..##................######..........##..##......######......######........................##..##........####..######......######..##..##........####..##..##........####....................................#### +// ####..##..##..............................##############....############............######........##############..........................##################....##############....##################..##################............................##..##..#### +// ####....##..............................##################..############............######........##############..........................##################....##############....##################..##################..............................##....#### +// ####..##..##........................................................................................................................................................................................................................................##..##..#### +// ####........................................................................................................................................................................................................................................................#### +// ################################################################################################################################################################################################################################################################ +// ################################################################################################################################################################################################################################################################ + +#include "images.h" + +const image_t img_RIP = { 128, 64, true, 837, 0x06, { // orig:1024, comp:18.26% + 0x06, 0x20, 0xFF, 0xC0, 0x06, 0x0E, 0x00, 0x03, 0xD4, 0x06, 0x0E, 0x00, 0x2B, 0xC8, 0x01, 0xFC, + 0x1E, 0x1F, 0xF0, 0x00, 0xFE, 0x20, 0x8F, 0xE3, 0xF8, 0xFE, 0x3F, 0x80, 0x13, 0xD4, 0x01, 0xFC, + 0x0E, 0x0F, 0xF0, 0x00, 0xFE, 0x71, 0xCF, 0xE3, 0xF8, 0xFE, 0x3F, 0x80, 0x2B, 0xC0, 0x00, 0x0E, + 0x0A, 0x00, 0x38, 0x01, 0x87, 0x71, 0xD8, 0x77, 0x1C, 0x07, 0x71, 0xC0, 0x03, 0xC0, 0x03, 0x8E, + 0x0A, 0x0E, 0x28, 0x01, 0xC5, 0x51, 0x5C, 0x77, 0x1D, 0xC7, 0x71, 0x40, 0x03, 0xC0, 0x03, 0x8A, + 0x0A, 0x0E, 0x28, 0x01, 0x47, 0x51, 0x5C, 0x55, 0x15, 0xC5, 0x51, 0x40, 0x03, 0xC0, 0x02, 0x8A, + 0x0A, 0x0A, 0x28, 0x01, 0x40, 0x51, 0x54, 0x55, 0x15, 0x45, 0x51, 0x40, 0x03, 0xC0, 0x02, 0x8A, + 0x0A, 0x0A, 0x28, 0x01, 0x40, 0x51, 0x54, 0x55, 0x15, 0x45, 0x51, 0xC0, 0x03, 0xC0, 0x02, 0x8E, + 0x0A, 0x0A, 0x38, 0x01, 0x40, 0x51, 0x54, 0x75, 0x55, 0x47, 0x50, 0x00, 0x03, 0xC0, 0x02, 0xF8, + 0x0A, 0x0B, 0xE0, 0x01, 0x40, 0x71, 0xD7, 0xC5, 0x15, 0x7C, 0x50, 0x00, 0x03, 0xC0, 0x02, 0xF8, + 0x0A, 0x0B, 0xE0, 0x01, 0x40, 0x3F, 0x97, 0xC5, 0x15, 0x7C, 0x57, 0x80, 0x03, 0xC0, 0x02, 0x9C, + 0x0A, 0x0A, 0x00, 0x01, 0x40, 0x1B, 0x14, 0x75, 0x55, 0x4E, 0x57, 0xC0, 0x03, 0xC0, 0x02, 0x94, + 0x0A, 0x0A, 0x00, 0x01, 0x40, 0x0A, 0x14, 0x55, 0x15, 0x4A, 0x51, 0x40, 0x03, 0xC0, 0x02, 0x94, + 0x0A, 0x0A, 0x00, 0x01, 0x40, 0x0A, 0x14, 0x55, 0x15, 0x4A, 0x51, 0x40, 0x03, 0xC0, 0x02, 0x94, + 0x0A, 0x0A, 0x00, 0x01, 0xC7, 0x0A, 0x14, 0x55, 0x15, 0x4A, 0x71, 0x40, 0x03, 0xC0, 0x02, 0x94, + 0x0A, 0x0A, 0x00, 0x01, 0xC5, 0x0A, 0x1C, 0x77, 0x1D, 0x4A, 0x71, 0x40, 0x03, 0xC0, 0x02, 0x94, + 0x0A, 0x0A, 0x00, 0x01, 0x87, 0x0E, 0x1C, 0x77, 0x1D, 0x4A, 0x61, 0xC0, 0x03, 0xC0, 0x03, 0x9C, + 0xCE, 0xCE, 0xC0, 0x00, 0xFE, 0x0E, 0x0F, 0xE3, 0xF9, 0xCE, 0x3F, 0x80, 0x03, 0xC0, 0x03, 0x8E, + 0xDE, 0xDE, 0xC0, 0x00, 0xFE, 0x1F, 0x0F, 0xE3, 0xF9, 0xC7, 0x3F, 0x80, 0x03, 0xC0, 0x06, 0x0E, + 0x00, 0x03, 0xC0, 0x06, 0x0E, 0x00, 0x03, 0xC0, 0x06, 0x0A, 0x00, 0x01, 0x8C, 0x07, 0xF0, 0x03, + 0xC0, 0x06, 0x07, 0x00, 0x04, 0x00, 0x00, 0x02, 0x52, 0x18, 0x0C, 0x03, 0xC1, 0xD5, 0xC7, 0x57, + 0x77, 0x6D, 0xC4, 0x5D, 0x2B, 0x8E, 0xE0, 0x03, 0x5A, 0x20, 0x02, 0x03, 0xC0, 0x95, 0x04, 0x54, + 0x24, 0x55, 0x04, 0x55, 0xA1, 0x0A, 0x80, 0x01, 0x8C, 0x47, 0xC1, 0x03, 0xC0, 0x9D, 0x87, 0x27, + 0x26, 0x55, 0xC5, 0x55, 0x61, 0x0C, 0xC0, 0x00, 0x50, 0x88, 0x21, 0x03, 0xC0, 0x95, 0x01, 0x21, + 0x24, 0x44, 0x45, 0x55, 0x21, 0x0A, 0x80, 0x00, 0x20, 0x90, 0x11, 0x03, 0xC0, 0x95, 0xC7, 0x27, + 0x27, 0x45, 0xC6, 0xDD, 0x21, 0x0E, 0xE0, 0x00, 0x70, 0x91, 0x91, 0x03, 0xC0, 0x06, 0x0B, 0x00, + 0x88, 0x92, 0x51, 0x03, 0xC0, 0x06, 0x0A, 0x00, 0x01, 0x08, 0x92, 0x91, 0x03, 0xC0, 0x06, 0x0A, + 0x00, 0x01, 0x08, 0x92, 0x11, 0x03, 0xC1, 0xD5, 0xC7, 0x76, 0xDC, 0x45, 0xDD, 0x5D, 0x5C, 0x57, + 0x50, 0x00, 0x87, 0x11, 0xE2, 0x03, 0xC0, 0x95, 0x04, 0x55, 0x50, 0x44, 0x89, 0x55, 0x48, 0x55, + 0x50, 0x00, 0x80, 0x88, 0x03, 0x03, 0xC0, 0x9D, 0x87, 0x75, 0x58, 0x54, 0x89, 0xD5, 0x48, 0x25, + 0x50, 0x00, 0x40, 0x7C, 0x04, 0x83, 0xC0, 0x95, 0x01, 0x54, 0x50, 0x54, 0x89, 0x55, 0x48, 0x25, + 0x50, 0x00, 0x40, 0x07, 0xF8, 0x43, 0xC0, 0x95, 0xC7, 0x54, 0x5C, 0x6D, 0xC9, 0x5D, 0xC8, 0x27, + 0x70, 0x00, 0x30, 0x00, 0x00, 0x43, 0xC0, 0x06, 0x0B, 0x00, 0x0F, 0xFF, 0xFF, 0x83, 0xC0, 0x06, + 0x0E, 0x00, 0x03, 0xC0, 0x06, 0x0E, 0x00, 0x03, 0xC0, 0x00, 0x07, 0xC7, 0xF1, 0xFC, 0x7F, 0x00, + 0x03, 0xF8, 0xFE, 0x3F, 0x8F, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x07, 0xC7, 0xF1, 0xFC, 0x7F, 0x00, + 0x03, 0xF8, 0xFE, 0x3F, 0x8F, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x05, 0x4E, 0x3B, 0x8E, 0xE3, 0x80, + 0x07, 0x1D, 0xC7, 0x71, 0xDC, 0x70, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x4E, 0x3A, 0x8E, 0xE3, 0x80, + 0x05, 0x15, 0xC7, 0x51, 0x54, 0x50, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x4A, 0x2B, 0x8A, 0xA2, 0x80, + 0x07, 0x15, 0x45, 0x71, 0x5C, 0x50, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x4A, 0x28, 0x0A, 0xA2, 0x80, + 0x00, 0x15, 0x45, 0x01, 0x40, 0x50, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x4A, 0x28, 0x0A, 0xA6, 0x80, + 0x00, 0x15, 0x4D, 0x01, 0x40, 0x50, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x4E, 0x28, 0x0E, 0xA6, 0x80, + 0x00, 0x1D, 0x4D, 0x01, 0xC0, 0x70, 0x00, 0x03, 0xC0, 0x00, 0x01, 0xC3, 0xE8, 0x0E, 0xAA, 0x9F, + 0xE1, 0xF9, 0x55, 0x1F, 0x87, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x01, 0xC3, 0xE8, 0x38, 0xAA, 0x90, + 0x23, 0xF1, 0x55, 0x3F, 0x0F, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x28, 0x38, 0xB2, 0x9F, + 0xE7, 0x01, 0x65, 0x70, 0x1C, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x28, 0x28, 0xB2, 0x80, + 0x05, 0x01, 0x65, 0x50, 0x14, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x28, 0x28, 0xA2, 0x80, + 0x05, 0x01, 0x45, 0x50, 0x14, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x28, 0x28, 0xA2, 0x80, + 0x05, 0x01, 0x45, 0x50, 0x14, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x38, 0x28, 0xE3, 0x80, + 0x05, 0x01, 0xC7, 0x50, 0x14, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x01, 0x40, 0x38, 0x28, 0xE3, 0x80, + 0x05, 0x0D, 0xC7, 0x50, 0xD4, 0x30, 0x00, 0x03, 0xD4, 0x00, 0x07, 0xF3, 0xF0, 0x38, 0x7F, 0x00, + 0x07, 0xFC, 0xFE, 0x7F, 0xDF, 0xF0, 0x00, 0x2B, 0xC8, 0x00, 0x0F, 0xFB, 0xF0, 0x38, 0x7F, 0x00, + 0x07, 0xFC, 0xFE, 0x7F, 0xDF, 0xF0, 0x00, 0x13, 0xD4, 0x06, 0x0E, 0x00, 0x2B, 0xC0, 0x06, 0x0E, + 0x00, 0x03, 0x06, 0x20, 0xFF +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_Cable.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_Cable.c new file mode 100644 index 000000000..2fc6b5f23 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_Cable.c @@ -0,0 +1,17 @@ +// ####..## +// ##..#### +// ####..## +// ##..#### +// ####..## +// ##..#### +// ####..## +// ##..#### +// ####..## +// ##..#### +// ####..## + +#include "images.h" + +const image_t img_cc_Cable = { 4, 11, true, 4, 0x00, { // orig:6, comp:33.33% + 0x00, 0x05, 0xDB, 0xD0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_Joy.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_Joy.c new file mode 100644 index 000000000..dd189cb7e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_Joy.c @@ -0,0 +1,25 @@ +// ................##................ +// ............##########............ +// ....############..############.... +// ....######..............######.... +// ....####..................####.... +// ....##......................##.... +// ..####......................####.. +// ..####......................####.. +// ####..........................#### +// ..####......................####.. +// ..####......................####.. +// ....##......................##.... +// ....####..................####.... +// ....######..............######.... +// ....############..############.... +// ............##########............ +// ................##................ + +#include "images.h" + +const image_t img_cc_Joy = { 17, 17, false, 37, 0, { + 0x00, 0x80, 0x01, 0xF0, 0x0F, 0xDF, 0x87, 0x01, 0xC3, 0x00, 0x61, 0x00, 0x11, 0x80, 0x0C, 0xC0, + 0x06, 0xC0, 0x01, 0xB0, 0x01, 0x98, 0x00, 0xC4, 0x00, 0x43, 0x00, 0x61, 0xC0, 0x70, 0xFD, 0xF8, + 0x07, 0xC0, 0x00, 0x80, 0x00 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_Main.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_Main.c new file mode 100644 index 000000000..8e7bd5ed9 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_Main.c @@ -0,0 +1,92 @@ +// ..................................................##################................................................................................................##################.................................................. +// ......................................############................##............##########....................................................##########............##................############...................................... +// ..................................####............................##..........##..........##......................####......................##..........##..........##............................####.................................. +// ................................##................................##############..........####################################################..........##############................................##................................ +// ............................######................................##..........##..........##......................####......................##..........##..........##................................######............................ +// ..........................##..##....................################..........##..........##......................####......................##..........##..........################....................##..##.......................... +// ........................##....##........############............................##########....................................................##########............................############........##....##........................ +// ......................##......##########........................................................................................................................................................##########......##...................... +// ....................##..........########################################################################################################################################################################..........##.................... +// ..................##......########....................................................................................................................................................................########......##.................. +// ................##....######................................................................................................................................................................................######....##................ +// ..............##....####........................................................................................................................................................................................####....##.............. +// ............##########............................................................................................................................................................................................##########............ +// ............######......................................................................................####......####..####..####....................................................................................######............ +// ..........######......................##################................................................####..##..####..................................................................######..........................######.......... +// ..........####........................##################................................................####..##..####..####..####....................................................##########..........................####.......... +// ........####..........................####..........####................................................####..##..####..####..####..................................................####......####..........................####........ +// ......######..........................####..........####..................................................####..####....####..####................................................####..........####........................######...... +// ......####............................####....##....####........................................................................................................................####....##..##....####........................####...... +// ......##..............................####....##....####........................................................................................................................####......##......####..........................####.... +// ....####..............................####....##....####........................................................................................................................####....##..##....####..........................####.... +// ....##..................##################..........##################............................................................................................######..........####..........####..........######..............##.... +// ..####..................##################..........##################..........................................................................................##########..........####......####..........##########............####.. +// ..##....................####......................................####..........................########........########........########......................####......####..........##########..........####......####............##.. +// ..##....................####......................................####........................####....####....####....####....####....####..................####..........####..........######..........####....##....####..........##.. +// ####....................####....######..................######....####........................##........##....##........##....##........##................####....##..##....####......................####....##..##....####........#### +// ##......................####......................................####........................##........##....##........##....##........##................####....######....####......................####....######....####..........## +// ##......................####......................................####........................####....####....####....####....####....####................####........##....####......................####....##..##....####..........## +// ##......................##################..........##################..........................########........########........########....................####....##....####..........######..........####..........####............## +// ##......................##################..........##################........................................................................................####......####..........##########..........####......####..............## +// ##....................................####....##....####........................................................................................................##########..........####......####..........##########................## +// ##....................................####....##....####..........................................................................................................######..........####..##......####..........######..................## +// ##....................................####....##....####........................................................................................................................####....##........####................................## +// ##....................................####..........####........................................................................................................................####....####......####................................## +// ##....................................####..........####........................................................................................................................####....##..##....####................................## +// ####..................................##################..........................................................................................................................####....####..####................................#### +// ..##..................................##################............................................................................................................................####......####..................................##.. +// ..##..................................................................................................................................................................................##########....................................##.. +// ..####..................................................................................................................................................................................######....................................####.. +// ....##............................................................................................................................................................................................................................##.... +// ....##............................................................................................................................................................................................................................##.... +// ....####........................................................................................................................................................................................................................####.... +// ......##........................................................................................................................................................................................................................##...... +// ......####....................................................................................................................................................................................................................####...... +// ........####................................................................................................................................................................................................................####........ +// ..........####............................................................................................................................................................................................................####.......... +// ............####........................................................................................................................................................................................................####............ +// ..............####....................................................................................................................................................................................................####.............. +// ................######............................................................................................................................................................................................######................ +// ....................####........................................................................................................................................................................................####.................... +// ......................######................................................................................................................................................................................######...................... +// ..........................########....................................................................................................................................................................########.......................... +// ................................########################################################################################################################################################################................................ + +#include "images.h" + +const image_t img_cc_Main = { 116, 53, true, 542, 0x05, { // orig:769, comp:29.52% + 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x05, 0x05, 0x00, 0x3F, 0xE0, 0x05, 0x04, 0x00, 0x01, 0xF8, 0x04, + 0x0F, 0x80, 0x00, 0x00, 0x1F, 0x02, 0x01, 0xF8, 0x05, 0x04, 0x00, 0x60, 0x00, 0x41, 0x04, 0x00, + 0x60, 0x02, 0x08, 0x20, 0x00, 0x60, 0x00, 0x00, 0x00, 0x08, 0x00, 0x07, 0xF0, 0x7F, 0xFF, 0xFF, + 0xE0, 0xFE, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x41, 0x04, 0x00, 0x60, 0x02, 0x08, + 0x20, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x50, 0x03, 0xFC, 0x10, 0x40, 0x06, 0x00, 0x20, 0x83, 0xFC, + 0x00, 0xA0, 0x00, 0x00, 0x09, 0x0F, 0xC0, 0x00, 0xF8, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x3F, 0x09, + 0x00, 0x00, 0x01, 0x1F, 0x05, 0x09, 0x00, 0x0F, 0x88, 0x00, 0x00, 0x20, 0x05, 0x0A, 0xFF, 0xF0, + 0x40, 0x00, 0x04, 0x78, 0x05, 0x09, 0x00, 0x01, 0xE2, 0x00, 0x00, 0x9C, 0x05, 0x0A, 0x00, 0x03, + 0x90, 0x00, 0x13, 0x05, 0x0B, 0x00, 0x0C, 0x80, 0x03, 0xE0, 0x05, 0x0B, 0x00, 0x7C, 0x00, 0x38, + 0x05, 0x05, 0x00, 0xC6, 0xD8, 0x05, 0x04, 0x00, 0x01, 0xC0, 0x07, 0x00, 0x1F, 0xF0, 0x00, 0x00, + 0x0D, 0x60, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x0E, 0x00, 0x60, 0x01, 0xFF, 0x00, 0x00, 0x00, 0xD6, + 0xD8, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x60, 0x0C, 0x00, 0x18, 0x30, 0x00, 0x00, 0x0D, 0x6D, 0x80, + 0x00, 0x00, 0x31, 0x80, 0x03, 0x01, 0xC0, 0x01, 0x83, 0x00, 0x00, 0x00, 0x6C, 0xD8, 0x00, 0x00, + 0x06, 0x0C, 0x00, 0x38, 0x18, 0x00, 0x19, 0x30, 0x05, 0x07, 0x00, 0xCA, 0x60, 0x01, 0x81, 0x00, + 0x01, 0x93, 0x05, 0x07, 0x00, 0x0C, 0x46, 0x00, 0x0C, 0x30, 0x00, 0x19, 0x30, 0x05, 0x07, 0x00, + 0xCA, 0x60, 0x00, 0xC2, 0x00, 0xFF, 0x83, 0xFE, 0x05, 0x05, 0x00, 0x07, 0x06, 0x0C, 0x1C, 0x04, + 0x60, 0x0F, 0xF8, 0x3F, 0xE0, 0x05, 0x05, 0x00, 0xF8, 0x31, 0x83, 0xE0, 0x64, 0x00, 0xC0, 0x00, + 0x06, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x18, 0xC1, 0xF0, 0x63, 0x02, 0x40, 0x0C, 0x00, 0x00, 0x60, + 0x01, 0x99, 0x99, 0x98, 0x03, 0x06, 0x0E, 0x0C, 0x98, 0x2C, 0x00, 0xCE, 0x00, 0xE6, 0x00, 0x10, + 0x90, 0x90, 0x80, 0x65, 0x30, 0x01, 0x94, 0xC3, 0x80, 0x0C, 0x00, 0x00, 0x60, 0x01, 0x09, 0x09, + 0x08, 0x06, 0x73, 0x00, 0x19, 0xCC, 0x18, 0x00, 0xC0, 0x00, 0x06, 0x00, 0x19, 0x99, 0x99, 0x80, + 0x61, 0x30, 0x01, 0x94, 0xC1, 0x80, 0x0F, 0xF8, 0x3F, 0xE0, 0x00, 0xF0, 0xF0, 0xF0, 0x03, 0x26, + 0x0E, 0x0C, 0x18, 0x18, 0x00, 0xFF, 0x83, 0xFE, 0x05, 0x05, 0x00, 0x18, 0xC1, 0xF0, 0x63, 0x01, + 0x80, 0x00, 0x19, 0x30, 0x05, 0x06, 0x00, 0xF8, 0x31, 0x83, 0xE0, 0x18, 0x00, 0x01, 0x93, 0x05, + 0x06, 0x00, 0x07, 0x06, 0x8C, 0x1C, 0x01, 0x80, 0x00, 0x19, 0x30, 0x05, 0x07, 0x00, 0xC8, 0x60, + 0x00, 0x18, 0x00, 0x01, 0x83, 0x05, 0x07, 0x00, 0x0C, 0xC6, 0x00, 0x01, 0x80, 0x00, 0x18, 0x30, + 0x05, 0x07, 0x00, 0xCA, 0x60, 0x00, 0x1C, 0x00, 0x01, 0xFF, 0x05, 0x07, 0x00, 0x06, 0x6C, 0x00, + 0x03, 0x40, 0x00, 0x1F, 0xF0, 0x05, 0x07, 0x00, 0x31, 0x80, 0x00, 0x24, 0x05, 0x0A, 0x00, 0x01, + 0xF0, 0x00, 0x02, 0x60, 0x05, 0x0A, 0x00, 0x0E, 0x00, 0x00, 0x62, 0x05, 0x0D, 0x00, 0x04, 0x20, + 0x05, 0x0D, 0x00, 0x43, 0x05, 0x0D, 0x00, 0x0C, 0x10, 0x05, 0x0D, 0x00, 0x81, 0x80, 0x05, 0x0C, + 0x00, 0x18, 0x0C, 0x05, 0x0C, 0x00, 0x03, 0x00, 0x60, 0x05, 0x0C, 0x00, 0x60, 0x03, 0x05, 0x0C, + 0x00, 0x0C, 0x00, 0x18, 0x05, 0x0B, 0x00, 0x01, 0x80, 0x00, 0xE0, 0x05, 0x0B, 0x00, 0x70, 0x00, + 0x03, 0x05, 0x0B, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x05, 0x0A, 0x00, 0x03, 0x80, 0x00, 0x00, 0x78, + 0x05, 0x09, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x05, 0x0A, 0xFF, 0xF0, 0x00, 0x00 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_btn_A1.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_btn_A1.c new file mode 100644 index 000000000..4d54cbf22 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_btn_A1.c @@ -0,0 +1,13 @@ +// ############## +// ######..###### +// ####..##..#### +// ####......#### +// ####..##..#### +// ############## +// ############## + +#include "images.h" + +const image_t img_cc_btn_A1 = { 7, 7, false, 7, 0, { + 0xFF, 0xDF, 0x5E, 0x3D, 0x7F, 0xFF, 0x80 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_btn_B1.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_btn_B1.c new file mode 100644 index 000000000..89d357282 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_btn_B1.c @@ -0,0 +1,13 @@ +// ############## +// ####..######## +// ####..######## +// ####....###### +// ####..##..#### +// ######....#### +// ############## + +#include "images.h" + +const image_t img_cc_btn_B1 = { 7, 7, false, 7, 0, { + 0xFF, 0xBF, 0x7E, 0x7D, 0x7C, 0xFF, 0x80 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_btn_X1.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_btn_X1.c new file mode 100644 index 000000000..a1e7f2876 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_btn_X1.c @@ -0,0 +1,13 @@ +// ############## +// ############## +// ####..##..#### +// ######..###### +// ####..##..#### +// ############## +// ############## + +#include "images.h" + +const image_t img_cc_btn_X1 = { 7, 7, false, 7, 0, { + 0xFF, 0xFF, 0x5F, 0x7D, 0x7F, 0xFF, 0x80 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_btn_Y1.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_btn_Y1.c new file mode 100644 index 000000000..01f66b4c7 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_btn_Y1.c @@ -0,0 +1,13 @@ +// ############## +// ############## +// ####..##..#### +// ####......#### +// ########..#### +// ######..###### +// ############## + +#include "images.h" + +const image_t img_cc_btn_Y1 = { 7, 7, false, 7, 0, { + 0xFF, 0xFF, 0x5E, 0x3F, 0x7D, 0xFF, 0x80 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_pad_LR1.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_pad_LR1.c new file mode 100644 index 000000000..698fcfdd6 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_pad_LR1.c @@ -0,0 +1,11 @@ +// ############## +// ############## +// ####......#### +// ############## +// ############## + +#include "images.h" + +const image_t img_cc_pad_LR1 = { 7, 5, false, 5, 0, { + 0xFF, 0xFF, 0x1F, 0xFF, 0xE0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_pad_UD1.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_pad_UD1.c new file mode 100644 index 000000000..b7d104ee4 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_pad_UD1.c @@ -0,0 +1,13 @@ +// ########## +// ########## +// ####..#### +// ####..#### +// ####..#### +// ########## +// ########## + +#include "images.h" + +const image_t img_cc_pad_UD1 = { 5, 7, false, 5, 0, { + 0xFF, 0xF7, 0xBD, 0xFF, 0xE0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_L1.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_L1.c new file mode 100644 index 000000000..cf4d7159b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_L1.c @@ -0,0 +1,12 @@ +// ......##############....##....##.. +// ..####..##....##....##....##....## +// ##....##....##....##....##....##.. +// ##..##....##....##....##....##.... +// ..##....##....##....############## +// ##....##############.............. + +#include "images.h" + +const image_t img_cc_trg_L1 = { 17, 6, false, 13, 0, { + 0x1F, 0xC9, 0x34, 0x92, 0x64, 0x92, 0x54, 0x92, 0x44, 0x93, 0xFC, 0xFE, 0x00 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_L2.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_L2.c new file mode 100644 index 000000000..9e61a64cc --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_L2.c @@ -0,0 +1,12 @@ +// ......##############..##..##..##.. +// ..####..##..##..##..##..##..##..## +// ####..##..##..##..##..##..##..##.. +// ##..##..##..##..##..##..##..##..## +// ..##..##..##..##..################ +// ##..##..############.............. + +#include "images.h" + +const image_t img_cc_trg_L2 = { 17, 6, true, 12, 0x01, { // orig:13, comp:7.69% + 0x1F, 0xD5, 0x35, 0x55, 0x75, 0x01, 0x04, 0x55, 0x57, 0xFD, 0x7E, 0x00 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_L3.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_L3.c new file mode 100644 index 000000000..1b06de5ee --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_L3.c @@ -0,0 +1,12 @@ +// ......############..####..####..## +// ..######..####..####..####..####.. +// ######..####..####..####..####..## +// ####..####..####..####..####..#### +// ##..####..####..################## +// ..####..############.............. + +#include "images.h" + +const image_t img_cc_trg_L3 = { 17, 6, false, 13, 0, { + 0x1F, 0xB6, 0xBB, 0x6D, 0xBB, 0x6D, 0xBB, 0x6D, 0xBB, 0x6F, 0xFB, 0x7E, 0x00 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_L4.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_L4.c new file mode 100644 index 000000000..12f877ab1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_L4.c @@ -0,0 +1,12 @@ +// ......############################ +// ..################################ +// ################################## +// ################################## +// ################################## +// ####################.............. + +#include "images.h" + +const image_t img_cc_trg_L4 = { 17, 6, true, 8, 0x01, { // orig:13, comp:38.46% + 0x1F, 0xFF, 0xBF, 0x01, 0x08, 0xFF, 0xFE, 0x00 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_R1.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_R1.c new file mode 100644 index 000000000..a196c0fe1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_R1.c @@ -0,0 +1,12 @@ +// ..##....##....##############...... +// ##....##....##....##....##..####.. +// ..##....##....##....##....##....## +// ....##....##....##....##....##..## +// ##############....##....##....##.. +// ..............##############....## + +#include "images.h" + +const image_t img_cc_trg_R1 = { 17, 6, false, 13, 0, { + 0x49, 0xFC, 0x49, 0x25, 0x92, 0x49, 0x24, 0x92, 0x5F, 0xE4, 0x90, 0x0F, 0xE4 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_R2.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_R2.c new file mode 100644 index 000000000..ea5458f39 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_R2.c @@ -0,0 +1,12 @@ +// ..##..##..##..##############...... +// ##..##..##..##..##..##..##..####.. +// ..##..##..##..##..##..##..##..#### +// ##..##..##..##..##..##..##..##..## +// ################..##..##..##..##.. +// ..............############..##..## + +#include "images.h" + +const image_t img_cc_trg_R2 = { 17, 6, false, 13, 0, { + 0x55, 0xFC, 0x55, 0x55, 0x95, 0x55, 0x75, 0x55, 0x5F, 0xF5, 0x50, 0x0F, 0xD4 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_R3.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_R3.c new file mode 100644 index 000000000..94cf02bf0 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_R3.c @@ -0,0 +1,12 @@ +// ##..####..####..############...... +// ..####..####..####..####..######.. +// ##..####..####..####..####..###### +// ####..####..####..####..####..#### +// ##################..####..####..## +// ..............############..####.. + +#include "images.h" + +const image_t img_cc_trg_R3 = { 17, 6, false, 13, 0, { + 0xB6, 0xFC, 0x36, 0xDB, 0xAD, 0xB6, 0xFB, 0x6D, 0xBF, 0xFB, 0x68, 0x0F, 0xD8 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_R4.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_R4.c new file mode 100644 index 000000000..c90fbe20b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_cc_trg_R4.c @@ -0,0 +1,12 @@ +// ############################...... +// ################################.. +// ################################## +// ################################## +// ################################## +// ..............#################### + +#include "images.h" + +const image_t img_cc_trg_R4 = { 17, 6, true, 11, 0x00, { // orig:13, comp:15.38% + 0xFF, 0xFC, 0x7F, 0xFF, 0xBF, 0x00, 0x05, 0xFF, 0xF8, 0x0F, 0xFC +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_csLogo_FULL.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_csLogo_FULL.c new file mode 100644 index 000000000..97f09ac17 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_csLogo_FULL.c @@ -0,0 +1,81 @@ +// ....##########################################........##..........##........##############........##############........##############........##############............................................................................................ +// ....##########################################......######......######......##############........##############........##############........##############............................................................................................ +// ############..............................######....######......######....####........######....######......######................######....######......######..............######..######..######...................................................... +// ############..............................##..##....##..##......##..##....######......######....######......######....######......######....######......##..##..............##......##........##........................................................ +// ############..............................##..##....##..##......##..##....######......##..##....##..##......##..##....######......##..##....##..##......##..##..............####....######....##........................................................ +// ####....####..............................##..##....##..##......##..##....##..##......##..##....##..##......##..##....##..##......##..##....##..##......##..##..............##..........##....##........................................................ +// ############..............................######....##..##......##..##....##..##......##..##....##..##......##..##....##..##......##..##....##..##......######..............######..######....##..##.................................................... +// ############........................................##..##......##..##....##..##......######....##..##..##..##..##....##..##......######....##..##...................................................................................................... +// ####....####........................................######......######....##..##########........##..##......##..##....##..##########........##..##........................####..######..######..##...................................................... +// ######..####........####..............................##############......##..##########........##..##......##..##....##..##########........##..##..########................##..##..##..##..##..##..##.................................................. +// ####..######........####................................####..####........##..##......######....##..##..##..##..##....##..##....######......##..##..##########..............##..######..######..######.................................................. +// ######..####................####..........................##..##..........##..##......##..##....##..##......##..##....##..##....##..##......##..##......##..##..............##......##..##..##......##.................................................. +// ####..######..............##....##........................##..##..........##..##......##..##....##..##......##..##....##..##....##..##......##..##......##..##............######....##..######......##..................................##.............. +// ######..####..............##....##........................##..##..........##..##......##..##....##..##......##..##....##..##....##..##......######......##..##........................................................................##..##............ +// ####..######................####..........................##..##..........######......######....######......######....##..##....##..##......######......##..##..................................................................######......##.......... +// ####....####..............................................######..........######......######....######......######....##..##....##..##......####........######..................................................................####....##....##........ +// ####....####..............................................######............##############........##############......######....######........##############....................................................................##....##..##....##...... +// ####....####............................................##########..........##############........##############......######......######......##############..............................................................######....##......##....##.... +// ####....####..............................................................................................................................................................................................................####........##..##..##....##.. +// ####....####..............................................................................................................................................................................................................##....##......##......##....## +// ####....####....................##############........##..........##........##############......##################......##############........##############........##############..................................######....##..........##..##....#### +// ####....####....................##############......######......######......##############......##################......##############........##############........##############..................................####....##......##......##....###### +// ####..######..................####........######....######......######....####........######....####..######..####....####....##########....##################....####........######................................##........##..##..##........######## +// ######..####..................######......##..##....##..##......##..##....######......##..##..........##..##..........######................######..##..######....######......##..##..........................######....##......##......##....########.. +// ####..######..................##..##......##..##....##..##......##..##....##..##......##..##..........##..##..........##..##................##..##..##..##..##....##..##......##..##..........................####....##..##..........##....##########.. +// ######..####..................##..##......##..##....##..##......##..##....##..##......##..##..........##..##..........##..##................##..##..##..##..##....##..##......##..##..........................##....##......##......##....############## +// ####..######..................##..##......######....##..##......##..##....##..##......######..........##..##..........##..##................##..##..##..##..##....##..##......######....................######............##..##........########....#### +// ######..####..................######................##..##......##..##....######......................##..##..........##..##....######......##..##..##..##..##....######................................####....................##....##########........ +// ####....####....................############........######......######......############..............##..##..........##..########..........##..##..##..##..##......############........................##....##..............##....##############...... +// ############........................##########........##############............##########............##..##..........##..########..........##..##..##..##..##..........##########....................##....##..##..........##....########....####...... +// ############..............................######........####..####....................######..........##..##..........##..##....######......##..##..##..##..##................######................##..##........##............##########.............. +// ####....####..................######......##..##..........##..##..........######......##..##..........##..##..........##..##................##..##......##..##....######......##..##................####............##..##....##############............ +// ############..................##..##......##..##..........##..##..........##..##......##..##..........##..##..........##..##................##..##..##..##..##....##..##......##..##................####....##........##....########....####............ +// ############..........####....##..##......##..##..........##..##..........##..##......##..##..........##..##..........##..##................##..##......##..##....##..##......##..##..................##########....##....##########.................... +// ############..........####....##..##......######..........##..##..........##..##......######..........##..##..........######................##..##......##..##....##..##......######....................######..........##############.................. +// ############..........####....######........####..........######..........######........####..........##..##..........####....##########....##..##......##..##....######........####......................####........########....####.................. +// ....######################......##############............######............##############............######............##############......######......######......##############..........................######..##########.......................... +// ....######################......##############..........##########..........##############............######............##############......######......######......##############............................##################........................ +// ................................................................................................................................................................................................................########....####........................ +// ..................................................................................................................................................................................................................####.................................. + +#include "images.h" + +const image_t img_csLogo_FULL = { 124, 40, true, 571, 0x0B, { // orig:620, comp:7.90% + 0x3F, 0xFF, 0xFE, 0x10, 0x43, 0xF8, 0x7F, 0x0F, 0xE1, 0xFC, 0x0B, 0x05, 0x00, 0x03, 0xFF, 0xFF, + 0xE3, 0x8E, 0x3F, 0x87, 0xF0, 0xFE, 0x1F, 0xC0, 0x0B, 0x05, 0x00, 0xFC, 0x00, 0x07, 0x38, 0xE6, + 0x1C, 0xE3, 0x80, 0x73, 0x8E, 0x03, 0xBB, 0x80, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x52, 0x8A, 0x71, + 0xCE, 0x39, 0xC7, 0x38, 0xA0, 0x22, 0x10, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x05, 0x28, 0xA7, 0x14, + 0xA2, 0x9C, 0x52, 0x8A, 0x03, 0x39, 0x00, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x52, 0x8A, 0x51, 0x4A, + 0x29, 0x45, 0x28, 0xA0, 0x20, 0x90, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x07, 0x28, 0xA5, 0x14, 0xA2, + 0x94, 0x52, 0x8E, 0x03, 0xB9, 0x40, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x02, 0x8A, 0x51, 0xCA, 0xA9, + 0x47, 0x28, 0x0B, 0x06, 0x00, 0xCC, 0x00, 0x00, 0x38, 0xE5, 0xF0, 0xA2, 0x97, 0xC2, 0x80, 0x06, + 0xEE, 0x80, 0x00, 0x00, 0x0E, 0xC3, 0x00, 0x01, 0xFC, 0x5F, 0x0A, 0x29, 0x7C, 0x2B, 0xC0, 0x2A, + 0xAA, 0x00, 0x00, 0x00, 0xDC, 0x30, 0x00, 0x0D, 0x85, 0x1C, 0xAA, 0x94, 0xE2, 0xBE, 0x02, 0xEE, + 0xE0, 0x00, 0x00, 0x0E, 0xC0, 0x30, 0x00, 0x50, 0x51, 0x4A, 0x29, 0x4A, 0x28, 0xA0, 0x22, 0xA2, + 0x00, 0x00, 0x00, 0xDC, 0x04, 0x80, 0x05, 0x05, 0x14, 0xA2, 0x94, 0xA2, 0x8A, 0x07, 0x2E, 0x20, + 0x00, 0x08, 0x0E, 0xC0, 0x48, 0x00, 0x50, 0x51, 0x4A, 0x29, 0x4A, 0x38, 0xA0, 0x00, 0x00, 0x00, + 0x01, 0x40, 0xDC, 0x03, 0x00, 0x05, 0x07, 0x1C, 0xE3, 0x94, 0xA3, 0x8A, 0x0B, 0x04, 0x00, 0xE2, + 0x0C, 0xC0, 0x00, 0x00, 0x70, 0x71, 0xCE, 0x39, 0x4A, 0x30, 0xE0, 0x00, 0x00, 0x00, 0x0C, 0x90, + 0xCC, 0x00, 0x00, 0x07, 0x03, 0xF8, 0x7F, 0x1C, 0xE1, 0xFC, 0x0B, 0x04, 0x00, 0x94, 0x8C, 0xC0, + 0x00, 0x00, 0xF8, 0x3F, 0x87, 0xF1, 0xC7, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x72, 0x24, 0xCC, 0x0B, + 0x0C, 0x00, 0x06, 0x15, 0x2C, 0xC0, 0x0B, 0x0C, 0x00, 0x48, 0x89, 0xCC, 0x00, 0xFE, 0x10, 0x43, + 0xF8, 0xFF, 0x8F, 0xE1, 0xFC, 0x3F, 0x80, 0x00, 0x39, 0x05, 0x3C, 0xC0, 0x0F, 0xE3, 0x8E, 0x3F, + 0x8F, 0xF8, 0xFE, 0x1F, 0xC3, 0xF8, 0x00, 0x03, 0x22, 0x27, 0xDC, 0x01, 0x87, 0x38, 0xE6, 0x1C, + 0xDD, 0x99, 0xF3, 0xFE, 0x61, 0xC0, 0x00, 0x21, 0x50, 0xFE, 0xC0, 0x1C, 0x52, 0x8A, 0x71, 0x41, + 0x41, 0xC0, 0x3A, 0xE7, 0x14, 0x00, 0x1C, 0x88, 0x9E, 0xDC, 0x01, 0x45, 0x28, 0xA5, 0x14, 0x14, + 0x14, 0x02, 0xAA, 0x51, 0x40, 0x01, 0x94, 0x13, 0xEE, 0xC0, 0x14, 0x52, 0x8A, 0x51, 0x41, 0x41, + 0x40, 0x2A, 0xA5, 0x14, 0x00, 0x12, 0x22, 0x7F, 0xDC, 0x01, 0x47, 0x28, 0xA5, 0x1C, 0x14, 0x14, + 0x02, 0xAA, 0x51, 0xC0, 0x0E, 0x05, 0x0F, 0x3E, 0xC0, 0x1C, 0x02, 0x8A, 0x70, 0x01, 0x41, 0x4E, + 0x2A, 0xA7, 0x00, 0x00, 0xC0, 0x09, 0xF0, 0xCC, 0x00, 0xFC, 0x38, 0xE3, 0xF0, 0x14, 0x17, 0x82, + 0xAA, 0x3F, 0x00, 0x09, 0x01, 0x3F, 0x8F, 0xC0, 0x03, 0xE1, 0xFC, 0x0F, 0x81, 0x41, 0x78, 0x2A, + 0xA0, 0xF8, 0x01, 0x28, 0x27, 0x98, 0xFC, 0x00, 0x07, 0x0D, 0x80, 0x1C, 0x14, 0x14, 0xE2, 0xAA, + 0x01, 0xC0, 0x28, 0x40, 0xF8, 0x0C, 0xC0, 0x1C, 0x50, 0x50, 0x71, 0x41, 0x41, 0x40, 0x28, 0xA7, + 0x14, 0x03, 0x02, 0x9F, 0xC0, 0xFC, 0x01, 0x45, 0x05, 0x05, 0x14, 0x14, 0x14, 0x02, 0xAA, 0x51, + 0x40, 0x32, 0x13, 0xCC, 0x0F, 0xC1, 0x94, 0x50, 0x50, 0x51, 0x41, 0x41, 0x40, 0x28, 0xA5, 0x14, + 0x01, 0xF2, 0x7C, 0x00, 0xFC, 0x19, 0x47, 0x05, 0x05, 0x1C, 0x14, 0x1C, 0x02, 0x8A, 0x51, 0xC0, + 0x0E, 0x0F, 0xE0, 0x0F, 0xC1, 0x9C, 0x30, 0x70, 0x70, 0xC1, 0x41, 0x9F, 0x28, 0xA7, 0x0C, 0x00, + 0x61, 0xE6, 0x00, 0x3F, 0xF8, 0xFE, 0x07, 0x03, 0xF8, 0x1C, 0x0F, 0xE3, 0x8E, 0x3F, 0x80, 0x03, + 0xBE, 0x00, 0x03, 0xFF, 0x8F, 0xE0, 0xF8, 0x3F, 0x81, 0xC0, 0xFE, 0x38, 0xE3, 0xF8, 0x00, 0x1F, + 0xF0, 0x0B, 0x0E, 0x00, 0xF3, 0x0B, 0x0E, 0x00, 0x06, 0x00, 0x00 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_csLogo_Small.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_csLogo_Small.c new file mode 100644 index 000000000..5cd2f613f --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_csLogo_Small.c @@ -0,0 +1,18 @@ +// ################## +// ################## +// ####..........#### +// ####..........#### +// ####..##.......... +// ####......######## +// ####......##....## +// ####......##...... +// ####......######## +// ####............## +// ########..##....## +// ########..######## + +#include "images.h" + +const image_t img_csLogo_Small = { 9, 12, false, 14, 0, { + 0xFF, 0xFF, 0xF0, 0x78, 0x3D, 0x06, 0x3F, 0x13, 0x88, 0xC7, 0xE0, 0x7D, 0x3E, 0xF0 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_ecp_SCL.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_ecp_SCL.c new file mode 100644 index 000000000..533b79c7b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_ecp_SCL.c @@ -0,0 +1,13 @@ +// ....##############......######## +// ....##############......######## +// ....####......####......####.... +// ....####......####......####.... +// ....####......####......####.... +// ########......##############.... +// ########......##############.... + +#include "images.h" + +const image_t img_ecp_SCL = { 16, 7, false, 14, 0, { + 0x3F, 0x8F, 0x3F, 0x8F, 0x31, 0x8C, 0x31, 0x8C, 0x31, 0x8C, 0xF1, 0xFC, 0xF1, 0xFC +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_ecp_SDA.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_ecp_SDA.c new file mode 100644 index 000000000..7384fc87c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_ecp_SDA.c @@ -0,0 +1,19 @@ +// ......##.......................... +// ....####.......................... +// ..####............................ +// ######################............ +// ######################....##...... +// ..####....................####.... +// ....####....................####.. +// ......##....###################### +// ............###################### +// ............................####.. +// ..........................####.... +// ..........................##...... + +#include "images.h" + +const image_t img_ecp_SDA = { 17, 12, false, 26, 0, { + 0x10, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1F, 0xFC, 0x0F, 0xFE, 0x43, 0x00, 0x30, 0xC0, 0x0C, 0x27, + 0xFF, 0x03, 0xFF, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x80 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_ecp_port.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_ecp_port.c new file mode 100644 index 000000000..942f0c593 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_ecp_port.c @@ -0,0 +1,66 @@ +// ....................##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..## +// ..................##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##.. +// ................##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..## +// ..............##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##.. +// ............##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..## +// ..........##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##.. +// ........##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..##..## +// ......######################################################################################################################..##..##..##.. +// ......########################################################################################################################..##..##..## +// ......####..............................................................................................................####..##..##..##.. +// ......####..............................................................................................................######..##..##..## +// ......####..............................................................................................................####..##..##..##.. +// ......####..............................................................................................................######..##..##..## +// ......####..............................................................................................................####..##..##..##.. +// ......####........##############################################################################################........######..##..##..## +// ......####........##############################################################################################........####..##..##..##.. +// ......####........####........####..........####........####..........####........####..........####........####........######..##..##..## +// ......####........####........####..........####........####..........####........####..........####........####........####..##..##..##.. +// ......####........####........##################........##################........##################........####........######..##..##..## +// ......####........####........##################........##################........##################........####........####..##..##..##.. +// ......####........####......................................................................................####........######..##..##..## +// ..########........####......................................................................................####........####..##########.. +// ##########........####......................................................................................####........################## +// ##########........####......................................................................................####........####..##########.. +// ..########........####......................................................................................####........################## +// ......####........####........##################..................................##################........####........####..##..##..##.. +// ......####........####........##################..................................##################........####........######..##..##..## +// ......####........####........####..........####........##################........####..........####........####........####..##..##..##.. +// ......####........####........####..........####........##..............##........####..........####........####........######..##..##..## +// ......####........##############################################################################################........####..##..##..##.. +// ......####........##############################################################################################........######..##..##..## +// ......####..............................................................................................................####..##..##..##.. +// ......####..............................................................................................................######..##..##..## +// ......####..............................................................................................................####..##..##..##.. +// ......####....................................######################################....................................######..##..##..## +// ......####....................................######################################....................................####..##..##..##.. +// ......####....................................####..##..##..##..................####....................................######..##..##.... +// ......####....................................######..##..##....................####....................................####..##..##...... +// ......####....................................####..##..##......................####....................................######..##........ +// ......####....................................######..##........................####....................................####..##.......... +// ......############################################..##..........................##############################################............ +// ......##############################################............................############################################.............. + +#include "images.h" + +const image_t img_ecp_port = { 69, 42, true, 290, 0x04, { // orig:363, comp:20.11% + 0x00, 0x2A, 0x04, 0x06, 0xAA, 0xA8, 0x02, 0x04, 0x07, 0xAA, 0x80, 0x2A, 0x04, 0x07, 0xAA, 0x02, + 0x04, 0x07, 0xAA, 0xA0, 0x2A, 0x04, 0x07, 0xAA, 0x82, 0x04, 0x07, 0xAA, 0xA8, 0x2A, 0x04, 0x07, + 0xAA, 0xA3, 0x04, 0x07, 0xFF, 0xAA, 0x1F, 0x04, 0x06, 0xFF, 0xFE, 0xA8, 0xC0, 0x04, 0x06, 0x00, + 0x6A, 0x86, 0x04, 0x06, 0x00, 0x03, 0xAA, 0x30, 0x04, 0x06, 0x00, 0x1A, 0xA1, 0x80, 0x04, 0x06, + 0x00, 0xEA, 0x8C, 0x04, 0x06, 0x00, 0x06, 0xA8, 0x61, 0x04, 0x05, 0xFF, 0xFC, 0x3A, 0xA3, 0x0F, + 0x04, 0x05, 0xFF, 0xE1, 0xAA, 0x18, 0x61, 0x83, 0x0C, 0x18, 0x60, 0xC3, 0x0E, 0xA8, 0xC3, 0x0C, + 0x18, 0x60, 0xC3, 0x06, 0x18, 0x6A, 0x86, 0x18, 0x7F, 0xC3, 0xFE, 0x1F, 0xF0, 0xC3, 0xAA, 0x30, + 0xC3, 0xFE, 0x1F, 0xF0, 0xFF, 0x86, 0x1A, 0xA1, 0x86, 0x04, 0x05, 0x00, 0x30, 0xEA, 0xBC, 0x30, + 0x04, 0x04, 0x00, 0x01, 0x86, 0xFB, 0xE1, 0x80, 0x04, 0x04, 0x00, 0x0C, 0x3F, 0xFF, 0x0C, 0x04, + 0x05, 0x00, 0x61, 0xBE, 0x78, 0x60, 0x04, 0x04, 0x00, 0x03, 0x0F, 0xF8, 0xC3, 0x0F, 0xF8, 0x00, + 0x03, 0xFE, 0x18, 0x6A, 0x86, 0x18, 0x7F, 0xC0, 0x00, 0x1F, 0xF0, 0xC3, 0xAA, 0x30, 0xC3, 0x06, + 0x1F, 0xF0, 0xC1, 0x86, 0x1A, 0xA1, 0x86, 0x18, 0x30, 0x80, 0x86, 0x0C, 0x30, 0xEA, 0x8C, 0x3F, + 0x04, 0x05, 0xFF, 0x86, 0xA8, 0x61, 0x04, 0x05, 0xFF, 0xFC, 0x3A, 0xA3, 0x04, 0x06, 0x00, 0x01, + 0xAA, 0x18, 0x04, 0x06, 0x00, 0x0E, 0xA8, 0xC0, 0x04, 0x06, 0x00, 0x6A, 0x86, 0x00, 0x00, 0x7F, + 0xFF, 0xF0, 0x00, 0x03, 0xAA, 0x30, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x1A, 0xA1, 0x80, 0x00, + 0x1A, 0xA0, 0x0C, 0x00, 0x00, 0xEA, 0x0C, 0x00, 0x00, 0xEA, 0x00, 0x60, 0x00, 0x06, 0xA0, 0x60, + 0x00, 0x06, 0xA0, 0x03, 0x00, 0x00, 0x3A, 0x03, 0x00, 0x00, 0x3A, 0x00, 0x18, 0x00, 0x01, 0xA0, + 0x1F, 0xFF, 0xFF, 0xA0, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFE, 0x00, 0x07, 0xFF, 0xFF, + 0xE0, 0x00 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_Back.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_Back.c new file mode 100644 index 000000000..d13bcf7f2 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_Back.c @@ -0,0 +1,15 @@ +// ..##############.. +// ################## +// ######..########## +// ####........###### +// ######..####..#### +// ############..#### +// ########....###### +// ################## +// ....############.. + +#include "images.h" + +const image_t img_key_Back = { 9, 9, false, 11, 0, { + 0x7F, 0x7F, 0xFB, 0xF8, 0x7E, 0xDF, 0xEF, 0xCF, 0xFF, 0x3F, 0x00 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_D.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_D.c new file mode 100644 index 000000000..8d182427c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_D.c @@ -0,0 +1,14 @@ +// ..##############.. +// ################## +// ################## +// ####..........#### +// ######......###### +// ########..######## +// ################## +// ..##############.. + +#include "images.h" + +const image_t img_key_D = { 9, 8, false, 9, 0, { + 0x7F, 0x7F, 0xFF, 0xF8, 0x3E, 0x3F, 0xBF, 0xFE, 0xFE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_L.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_L.c new file mode 100644 index 000000000..1fc5556b1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_L.c @@ -0,0 +1,15 @@ +// ..############.. +// ################ +// ########..###### +// ######....###### +// ####......###### +// ######....###### +// ########..###### +// ################ +// ..############.. + +#include "images.h" + +const image_t img_key_L = { 8, 9, false, 9, 0, { + 0x7E, 0xFF, 0xF7, 0xE7, 0xC7, 0xE7, 0xF7, 0xFF, 0x7E +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_OK.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_OK.c new file mode 100644 index 000000000..ef64128f8 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_OK.c @@ -0,0 +1,15 @@ +// ..##############.. +// ################## +// ######......###### +// ####..........#### +// ####..........#### +// ####..........#### +// ######......###### +// ################## +// ....############.. + +#include "images.h" + +const image_t img_key_OK = { 9, 9, false, 11, 0, { + 0x7F, 0x7F, 0xF8, 0xF8, 0x3C, 0x1E, 0x0F, 0x8F, 0xFF, 0x3F, 0x00 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_OKi.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_OKi.c new file mode 100644 index 000000000..595f2f431 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_OKi.c @@ -0,0 +1,15 @@ +// ..##############.. +// ####..........#### +// ##....######....## +// ##..##########..## +// ##..##########..## +// ##..##########..## +// ##....######....## +// ####..........#### +// ..##############.. + +#include "images.h" + +const image_t img_key_OKi = { 9, 9, false, 11, 0, { + 0x7F, 0x60, 0xE7, 0x37, 0xDB, 0xED, 0xF6, 0x73, 0x83, 0x7F, 0x00 +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_R.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_R.c new file mode 100644 index 000000000..87cc385bc --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_R.c @@ -0,0 +1,15 @@ +// ..############.. +// ################ +// ######..######## +// ######....###### +// ######......#### +// ######....###### +// ######..######## +// ################ +// ..############.. + +#include "images.h" + +const image_t img_key_R = { 8, 9, false, 9, 0, { + 0x7E, 0xFF, 0xEF, 0xE7, 0xE3, 0xE7, 0xEF, 0xFF, 0x7E +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_U.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_U.c new file mode 100644 index 000000000..aca5bb62a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_U.c @@ -0,0 +1,14 @@ +// ..##############.. +// ################## +// ########..######## +// ######......###### +// ####..........#### +// ################## +// ################## +// ..##############.. + +#include "images.h" + +const image_t img_key_U = { 9, 8, false, 9, 0, { + 0x7F, 0x7F, 0xFD, 0xFC, 0x7C, 0x1F, 0xFF, 0xFE, 0xFE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_Ui.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_Ui.c new file mode 100644 index 000000000..b740780ad --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/gfx/img_key_Ui.c @@ -0,0 +1,14 @@ +// ..##############.. +// ####..........#### +// ##......##......## +// ##....######....## +// ##..##########..## +// ##..............## +// ####..........#### +// ..##############.. + +#include "images.h" + +const image_t img_key_Ui = { 9, 8, false, 9, 0, { + 0x7F, 0x60, 0xE2, 0x33, 0x9B, 0xEC, 0x07, 0x06, 0xFE +}}; diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/i2c_workaround.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/i2c_workaround.h new file mode 100644 index 000000000..de1dbba54 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/i2c_workaround.h @@ -0,0 +1,119 @@ +/* + As of the date of releasing this code, there is (seemingly) a bug in the FZ i2c library code + It is described here: https://github.com/flipperdevices/flipperzero-firmware/issues/1670 + + This is a short-term workaround so I can keep developing while we get to the bottom of the issue + + FYI. *something* in the following code is the fix + +void furi_hal_i2c_acquire (FuriHalI2cBusHandle* handle) +{ + // 1. Disable the power/backlight (it uses i2c) + furi_hal_power_insomnia_enter(); + // 2. Lock bus access + handle->bus->callback(handle->bus, FuriHalI2cBusEventLock); + // 3. Ensuree that no active handle set + furi_check(handle->bus->current_handle == NULL); + // 4. Set current handle + handle->bus->current_handle = handle; + // 5. Activate bus + handle->bus->callback(handle->bus, FuriHalI2cBusEventActivate); + // 6. Activate handle + handle->callback(handle, FuriHalI2cBusHandleEventActivate); +} + +void furi_hal_i2c_release (FuriHalI2cBusHandle* handle) +{ + // Ensure that current handle is our handle + furi_check(handle->bus->current_handle == handle); + // 6. Deactivate handle + handle->callback(handle, FuriHalI2cBusHandleEventDeactivate); + // 5. Deactivate bus + handle->bus->callback(handle->bus, FuriHalI2cBusEventDeactivate); + // 3,4. Reset current handle + handle->bus->current_handle = NULL; + // 2. Unlock bus + handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock); + // 1. Re-enable the power system + furi_hal_power_insomnia_exit(); +} + +*/ + +#ifndef I2C_WORKAROUND_H_ +#define I2C_WORKAROUND_H_ + +#include + +#define ENABLE_WORKAROUND 1 + +#if ENABLE_WORKAROUND == 1 + //+============================================================================ ======================================== + static inline + bool furi_hal_Wi2c_is_device_ready (FuriHalI2cBusHandle* const bus, const uint8_t addr, const uint32_t tmo) + { + furi_hal_i2c_acquire(bus); + bool rv = furi_hal_i2c_is_device_ready(bus, addr, tmo); + furi_hal_i2c_release(bus); + return rv; + } + + //+============================================================================ + static inline + bool furi_hal_Wi2c_tx ( FuriHalI2cBusHandle* const bus, const uint8_t addr, + const void* buf, const size_t len, const uint32_t tmo ) + { + furi_hal_i2c_acquire(bus); + bool rv = furi_hal_i2c_tx(bus, addr, buf, len, tmo); + furi_hal_i2c_release(bus); + return rv; + } + + //+============================================================================ + static inline + bool furi_hal_Wi2c_rx ( FuriHalI2cBusHandle* const bus, const uint8_t addr, + void* buf, const size_t len, const uint32_t tmo ) + { + furi_hal_i2c_acquire(bus); + bool rv = furi_hal_i2c_rx(bus, addr, buf, len, tmo); + furi_hal_i2c_release(bus); + return rv; + } + + //+============================================================================ + static inline + bool furi_hal_Wi2c_trx ( FuriHalI2cBusHandle* const bus, const uint8_t addr, + const void* tx, const size_t txlen, + void* rx, const size_t rxlen, const uint32_t tmo ) + { + bool rv = furi_hal_Wi2c_tx(bus, addr, tx, txlen, tmo); + if (rv) rv = furi_hal_Wi2c_rx(bus, addr, rx, rxlen, tmo); + return rv; + } + + //----------------------------------------------------------------------------- ---------------------------------------- +# define furi_hal_i2c_is_device_ready(...) furi_hal_Wi2c_is_device_ready(__VA_ARGS__) +# define furi_hal_i2c_tx(...) furi_hal_Wi2c_tx(__VA_ARGS__) +# define furi_hal_i2c_rx(...) furi_hal_Wi2c_rx(__VA_ARGS__) +# define furi_hal_i2c_trx(...) furi_hal_Wi2c_trx(__VA_ARGS__) + +#endif //ENABLE_WORKAROUND + +//+============================================================================ ======================================== +// Some devices take a moment to respond to read requests +// The puts a delay between the address being set and the data being read +// +static inline +bool furi_hal_i2c_trxd ( FuriHalI2cBusHandle* const bus, const uint8_t addr, + const void* tx, const size_t txlen, + void* rx, const size_t rxlen, const uint32_t tmo, const uint32_t us ) +{ + bool rv = furi_hal_i2c_tx(bus, addr, tx, txlen, tmo); + if (rv) { + furi_delay_us(us); + rv = furi_hal_i2c_rx(bus, addr, rx, rxlen, tmo); + } + return rv; +} + +#endif //I2C_WORKAROUND_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/info.sh b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/info.sh new file mode 100644 index 000000000..e009eb118 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/info.sh @@ -0,0 +1,11 @@ +echo "MARKED AS TODO" +echo "==============" +grep //! *.c *.h + +echo -e "\nSUPPORTED CONTROLLERS" +echo "=====================" +grep '\[PID_.*{ {' wii_ec.c | head -n -3 | sed 's/\s*\(.*\)/\1/' + +echo -e "\nLOGGING" +echo "=======" +grep LOG_LEVEL *.h | grep -v '#if ' diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/notes.txt b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/notes.txt new file mode 100644 index 000000000..61b6e29af --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/notes.txt @@ -0,0 +1,87 @@ +//+============================================================================ ======================================== +// Select font +// A full list of u8g2 fonts can be found here: +// https://github.com/olikraus/u8g2/wiki/fntlistall +// ...and here are the ones available in FZ (currently: all of them): +// grep -P '.*u8g2.*\[[0-9]*\]' lib/u8g2/u8g2_fonts.c | sed 's/.*\(u8g2_.*\)\[.*/\1/' +// +#if 0 //! Extra fonts is just too memory hungry +#include +void setFont (Canvas* const canvas, const uint8_t* font) +{ + u8g2_SetFontMode(&canvas->fb, 1); // no idea - but canvas.c does it + u8g2_SetFont(&canvas->fb, font); +} +#endif + +litui : @BlueChip for posterity, the function to break at is flipper_application_spawn. At that point, you can set new breakpoints in your fap code and continue. + +/* + +This is wrong on quite a few levels! +https://training.ti.com/introduction-i2c-reserved-addresses + +void doit (void) +{ + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + printf("Scanning external i2c on PC0(SCL)/PC1(SDA)\r\n" + "Clock: 100khz, 7bit address\r\n" + "\r\n"); + printf(" | 0 1 2 3 4 5 6 7 8 9 A B C D E F\r\n"); + printf("--+--------------------------------\r\n"); + for(uint8_t row = 0; row < 0x8; row++) { + printf("%x | ", row); + for(uint8_t column = 0; column <= 0xF; column++) { + bool ret = furi_hal_i2c_is_device_ready( + &furi_hal_i2c_handle_external, ((row << 4) + column) << 1, 2); + printf("%c ", ret ? '#' : '-'); + } + printf("\r\n"); + } + furi_hal_i2c_release(&furi_hal_i2c_handle_external); +} +*/ + + +region locking : firmware/targets/f7/furi_hal/furi_hal_region.c + + +# if 0 //! scrolling works beautifully, but the LCD refresh can't keep up :( + // Waveform + if (cnt) { // start + for (int a = ACC_1; a < ACC_N; a++) { + canvas_draw_dot(canvas, x,y[a]+v[a][idx]); + for (int i = 1; i < aw -cnt; i++) { + canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]); + } + } + } else { // scroll + for (int a = ACC_1; a < ACC_N; a++) { + for (int i = 0; i < aw; i++) { + int off = (idx +i) %aw; + int prev = off ? off-1 : aw-1; + canvas_draw_line(canvas, x+i,y[a]+v[a][prev] , x+i,y[a]+v[a][off]); + } + } + } + +# else + int end = idx ? idx : aw; + for (int a = ACC_1; a < ACC_N; a++) { + canvas_draw_dot(canvas, x,y[a]+v[a][idx]); + if (state->apause) { + for (int i = 1; i < end; i++) + canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]); + } else { + for (int i = 1; i < end; i++) + canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]); + for (int i = end+10; i < aw -cnt; i++) + canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]); + } + } + + // Wipe bar + if (end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1); + if (++end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1); + if (++end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1); +# endif diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal.c new file mode 100644 index 000000000..fdf718b63 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal.c @@ -0,0 +1,540 @@ +//----------------------------------------------------------------------------- ---------------------------------------- +// Includes +// + +// System libs +#include // malloc +#include // uint32_t +#include // __VA_ARGS__ +#include +#include + +// FlipperZero libs +#include // Core API +#include // GUI (screen/keyboard) API +#include // GUI Input extensions +#include + +// Do this first! +#define ERR_C_ // Do this in precisely ONE file +#include "err.h" // Error numbers & messages + +#include "bc_logging.h" + +// Local headers +#include "wii_anal.h" // Various enums and struct declarations +#include "wii_i2c.h" // Wii i2c functions +#include "wii_ec.h" // Wii Extension Controller functions (eg. draw) +#include "wii_anal_keys.h" // key mappings +#include "gfx/images.h" // Images +#include "wii_anal_lcd.h" // Drawing functions +#include "wii_anal_ec.h" // Wii controller events + +#include "wii_anal_ver.h" // Version number + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// OOOOO // SSSSS CCCCC AAA L L BBBB AAA CCCC K K SSSSS +// O O /// S C A A L L B B A A C K K S +// O O /// SSSSS C AAAAA L L BBBB AAAAA C KKK SSSSS +// O O /// S C A A L L B B A A C K K S +// OOOOO // SSSSS CCCCC A A LLLLL LLLLL BBBB A A CCCC K K SSSSS +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +//+============================================================================ ======================================== +// OS Callback : Timer tick +// We register this function to be called when the OS signals a timer 'tick' event +// +static +void cbTimer (FuriMessageQueue* queue) +{ + ENTER; + furi_assert(queue); + + eventMsg_t message = {.id = EVID_TICK}; + furi_message_queue_put(queue, &message, 0); + + LEAVE; + return; +} + +//+============================================================================ ======================================== +// OS Callback : Keypress +// We register this function to be called when the OS detects a keypress +// +static +void cbInput (InputEvent* event, FuriMessageQueue* queue) +{ + ENTER; + furi_assert(queue); + furi_assert(event); + + // Put an "input" event message on the message queue + eventMsg_t message = {.id = EVID_KEY, .input = *event}; + furi_message_queue_put(queue, &message, FuriWaitForever); + + LEAVE; + return; +} + +//+============================================================================ +// Show version number +// +static +void showVer (Canvas* const canvas) +{ + show(canvas, 0,59, &img_3x5_v, SHOW_SET_BLK); + show(canvas, 4,59, VER_MAJ, SHOW_SET_BLK); + canvas_draw_frame(canvas, 8,62, 2,2); + show(canvas, 11,59, VER_MIN, SHOW_SET_BLK); +} + +//+============================================================================ +// OS Callback : Draw request +// We register this function to be called when the OS requests that the screen is redrawn +// +// We actually instruct the OS to perform this request, after we update the interface +// I guess it's possible that this instruction may able be issued by other threads !? +// +static +void cbDraw (Canvas* const canvas, void* ctx) +{ + ENTER; + furi_assert(canvas); + furi_assert(ctx); + + state_t* state = NULL; + + // Try to acquire the mutex for the plugin state variables, timeout = 25mS + if ( !(state = (state_t*)acquire_mutex((ValueMutex*)ctx, 25)) ) return ; + + switch (state->scene) { + //--------------------------------------------------------------------- + case SCENE_SPLASH: + show(canvas, 2,0, &img_csLogo_FULL, SHOW_SET_BLK); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64,43, AlignCenter, AlignTop, "Wii Extension Controller"); + canvas_draw_str_aligned(canvas, 64,55, AlignCenter, AlignTop, "Protocol Analyser"); + + showVer(canvas); + + break; + + //--------------------------------------------------------------------- + case SCENE_RIP: + show(canvas, 0,0, &img_RIP, SHOW_SET_BLK); + break; + + //--------------------------------------------------------------------- + case SCENE_WAIT: +# define xo 2 + + show(canvas, 3+xo,10, &img_ecp_port, SHOW_SET_BLK); + + BOX_TL(22+xo, 6, 82+xo,23); // 3v3 + BOX_TL(48+xo,21, 82+xo,23); // C1 + BOX_BL(22+xo,41, 82+xo,58); // C0 + BOX_BL(48+xo,41, 82+xo,44); // Gnd + + show(canvas, 90+xo, 3, &img_6x8_3, SHOW_SET_BLK); // 3v3 + show(canvas, 97+xo, 3, &img_6x8_v, SHOW_SET_BLK); + show(canvas, 104+xo, 3, &img_6x8_3, SHOW_SET_BLK); + + show(canvas, 90+xo,18, &img_6x8_C, SHOW_SET_BLK); // C1 <-> + show(canvas, 98+xo,18, &img_6x8_1, SHOW_SET_BLK); + show(canvas, 107+xo,16, &img_ecp_SDA, SHOW_SET_BLK); + + show(canvas, 90+xo,40, &img_6x8_G, SHOW_SET_BLK); // Gnd + show(canvas, 97+xo,40, &img_6x8_n, SHOW_SET_BLK); + show(canvas, 104+xo,40, &img_6x8_d, SHOW_SET_BLK); + + show(canvas, 90+xo,54, &img_6x8_C, SHOW_SET_BLK); // C0 _-_- + show(canvas, 98+xo,54, &img_6x8_0, SHOW_SET_BLK); + show(canvas, 108+xo,54, &img_ecp_SCL, SHOW_SET_BLK); + + show(canvas, 0,0, &img_csLogo_Small, SHOW_SET_BLK); + showVer(canvas); + +# undef xo + break; + + //--------------------------------------------------------------------- + case SCENE_DEBUG: + canvas_set_font(canvas, FontSecondary); + + show(canvas, 0,0, &img_key_U, SHOW_SET_BLK); + canvas_draw_str_aligned(canvas, 11, 0, AlignLeft, AlignTop, "Initialise Perhipheral"); + + show(canvas, 0,11, &img_key_OK, SHOW_SET_BLK); + canvas_draw_str_aligned(canvas, 11,11, AlignLeft, AlignTop, "Read values [see log]"); + + show(canvas, 0,23, &img_key_D, SHOW_SET_BLK); + canvas_draw_str_aligned(canvas, 11,22, AlignLeft, AlignTop, "Restart Scanner"); + + show(canvas, 0,33, &img_key_Back, SHOW_SET_BLK); + canvas_draw_str_aligned(canvas, 11,33, AlignLeft, AlignTop, "Exit"); + + break ; + + //--------------------------------------------------------------------- + default: + if (state->ec.pidx >= PID_ERROR) { + ERROR("%s : bad PID = %d", __func__, state->ec.pidx); + } else { + if ((state->scene == SCENE_DUMP) || !ecId[state->ec.pidx].show) + ecId[PID_UNKNOWN].show(canvas, state); + else + ecId[state->ec.pidx].show(canvas, state); + } + break; + } + + // Release the mutex + release_mutex((ValueMutex*)ctx, state); + + LEAVE; + return; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// SSSSS TTTTT AAA TTTTT EEEEE V V AAA RRRR IIIII AAA BBBB L EEEEE SSSSS +// S T A A T E V V A A R R I A A B B L E S +// SSSSS T AAAAA T EEE V V AAAAA RRRR I AAAAA BBBB L EEE SSSSS +// S T A A T E V V A A R R I A A B B L E S +// SSSSS T A A T EEEEE V A A R R IIIII A A BBBB LLLLL EEEEE SSSSS +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +//+============================================================================ ======================================== +// Initialise plugin state variables +// +static inline +bool stateInit (state_t* const state) +{ + ENTER; + furi_assert(state); + + bool rv = true; // assume success + + // Enable the main loop + state->run = true; + + // Timer + state->timerEn = false; + state->timer = NULL; + state->timerHz = furi_kernel_get_tick_frequency(); + state->fps = 30; + + // Scene + state->scene = SCENE_SPLASH; + state->scenePrev = SCENE_NONE; + state->scenePegg = SCENE_NONE; + + state->hold = 0; // show hold meters (-1=lowest, 0=current, +1=highest} + state->calib = CAL_TRACK; + state->pause = false; // animation running + state->apause = false; // auto-pause animation + + // Notifications + state->notify = NULL; + + // Perhipheral + state->ec.init = false; + state->ec.pidx = PID_UNKNOWN; + state->ec.sid = ecId[state->ec.pidx].name; + + // Controller data + memset(state->ec.pid, 0xC5, PID_LEN); // Cyborg 5ystems + memset(state->ec.calF, 0xC5, CAL_LEN); + memset(state->ec.joy, 0xC5, JOY_LEN); + + // Encryption details + state->ec.encrypt = false; + memset(state->ec.encKey, 0x00, ENC_LEN); + + // Seed the PRNG + // CYCCNT --> lib/STM32CubeWB/Drivers/CMSIS/Include/core_cm7.h +// srand(DWT->CYCCNT); + + LEAVE; + return rv; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// MM MM AAA IIIII N N +// M M M A A I NN N +// M M M AAAAA I N N N +// M M A A I N NN +// M M A A IIIII N N +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +//+============================================================================ ======================================== +// Enable/Disable scanning +// +void timerEn (state_t* state, bool on) +{ + ENTER; + furi_assert(state); + + // ENable scanning + if (on) { + if (state->timerEn) { + WARN(wii_errs[WARN_SCAN_START]); + } else { + // Set the timer to fire at 'fps' times/second + if (furi_timer_start(state->timer, state->timerHz/state->fps) == FuriStatusOk) { + state->timerEn = true; + INFO("%s : monitor started", __func__); + } else { + ERROR(wii_errs[ERR_TIMER_START]); + } + } + + // DISable scanning + } else { + if (!state->timerEn) { + WARN(wii_errs[WARN_SCAN_STOP]); + } else { + // Stop the timer + if (furi_timer_stop(state->timer) == FuriStatusOk) { + state->timerEn = false; + INFO("%s : monitor stopped", __func__); + } else { + ERROR(wii_errs[ERR_TIMER_STOP]); + } + } + } + + LEAVE; + return; +} + +//+============================================================================ ======================================== +// Plugin entry point +// +int32_t wii_ec_anal (void) +{ + ENTER; + + // ===== Variables ===== + err_t error = 0; // assume success + Gui* gui = NULL; + ViewPort* vpp = NULL; + state_t* state = NULL; + ValueMutex mutex = {0}; + FuriMessageQueue* queue = NULL; + const uint32_t queueSz = 20; // maximum messages in queue + uint32_t tmo = (3.5f *1000); // timeout splash screen after N seconds + + // The queue will contain plugin event-messages + // --> local + eventMsg_t msg = {0}; + + INFO("BEGIN"); + + // ===== Message queue ===== + // 1. Create a message queue (for up to 8 (keyboard) event messages) + if ( !(queue = furi_message_queue_alloc(queueSz, sizeof(msg))) ) { + ERROR(wii_errs[(error = ERR_MALLOC_QUEUE)]); + goto bail; + } + + // ===== Create GUI Interface ===== + // 2. Create a GUI interface + if ( !(gui = furi_record_open("gui")) ) { + ERROR(wii_errs[(error = ERR_NO_GUI)]); + goto bail; + } + + // ===== Plugin state variables ===== + // 3. Allocate space on the heap for the plugin state variables + if ( !(state = malloc(sizeof(state_t))) ) { + ERROR(wii_errs[(error = ERR_MALLOC_STATE)]); + goto bail; + } + // 4. Initialise the plugin state variables + if (!stateInit(state)) { + // error message(s) is/are output by stateInit() + error = 15; + goto bail; + } + // 5. Create a mutex for (reading/writing) the plugin state variables + if (!init_mutex(&mutex, state, sizeof(state))) { + ERROR(wii_errs[(error = ERR_NO_MUTEX)]); + goto bail; + } + + // ===== Viewport ===== + // 6. Allocate space on the heap for the viewport + if ( !(vpp = view_port_alloc()) ) { + ERROR(wii_errs[(error = ERR_MALLOC_VIEW)]); + goto bail; + } + // 7a. Register a callback for input events + view_port_input_callback_set(vpp, cbInput, queue); + // 7b. Register a callback for draw events + view_port_draw_callback_set(vpp, cbDraw, &mutex); + + // ===== Start GUI Interface ===== + // 8. Attach the viewport to the GUI + gui_add_view_port(gui, vpp, GuiLayerFullscreen); + + // ===== Timer ===== + // 9. Allocate a timer + if ( !(state->timer = furi_timer_alloc(cbTimer, FuriTimerTypePeriodic, queue)) ) { + ERROR(wii_errs[(error = ERR_NO_TIMER)]); + goto bail; + } + + // === System Notifications === + // 10. Acquire a handle for the system notification queue + if ( !(state->notify = furi_record_open(RECORD_NOTIFICATION)) ) { + ERROR(wii_errs[(error = ERR_NO_NOTIFY)]); + goto bail; + } + patBacklight(state); // Turn on the backlight [qv. remote FAP launch] + + INFO("INITIALISED"); + + // ==================== Main event loop ==================== + + if (state->run) do { + bool redraw = false; + FuriStatus status = FuriStatusErrorTimeout; + + // Wait for a message +// while ((status = furi_message_queue_get(queue, &msg, tmo)) == FuriStatusErrorTimeout) ; + status = furi_message_queue_get(queue, &msg, tmo); + + // Clear splash screen + if ( (state->scene == SCENE_SPLASH) && (state->scenePrev == SCENE_NONE) && // Initial splash + ( (status == FuriStatusErrorTimeout) || // timeout + ((msg.id == EVID_KEY) && (msg.input.type == InputTypeShort)) ) // or key-short + ) { + tmo = 60 *1000; // increase message-wait timeout to 60secs + timerEn(state, true); // start scanning the i2c bus + status = FuriStatusOk; // pass status check + msg.id = EVID_NONE; // valid msg ID + sceneSet(state, SCENE_WAIT); // move to wait screen + } + + // Check for queue errors + if (status != FuriStatusOk) { + switch (status) { + case FuriStatusErrorTimeout: DEBUG(wii_errs[DEBUG_QUEUE_TIMEOUT]); continue ; + case FuriStatusError: ERROR(wii_errs[(error = ERR_QUEUE_RTOS)]); goto bail ; + case FuriStatusErrorResource: ERROR(wii_errs[(error = ERR_QUEUE_RESOURCE)]); goto bail ; + case FuriStatusErrorParameter: ERROR(wii_errs[(error = ERR_QUEUE_BADPRM)]); goto bail ; + case FuriStatusErrorNoMemory: ERROR(wii_errs[(error = ERR_QUEUE_NOMEM)]); goto bail ; + case FuriStatusErrorISR: ERROR(wii_errs[(error = ERR_QUEUE_ISR)]); goto bail ; + default: ERROR(wii_errs[(error = ERR_QUEUE_UNK)]); goto bail ; + } + } + // Read successful + + // *** Try to lock the plugin state variables *** + if ( !(state = (state_t*)acquire_mutex_block(&mutex)) ) { + ERROR(wii_errs[(error = ERR_MUTEX_BLOCK)]); + goto bail; + } + + // *** Handle events *** + switch (msg.id) { + //--------------------------------------------- + case EVID_TICK: // Timer events + //! I would prefer to have ecPoll() called by cbTimer() + //! ...but how does cbTimer() get the required access to the state variables? Namely: 'state->ec' + //! So, for now, the timer pushes a message to call ecPoll() + //! which, in turn, will push WIIEC event meesages! + ecPoll(&state->ec, queue); + break; + + //--------------------------------------------- + case EVID_WIIEC: // WiiMote Perhipheral + if (evWiiEC(&msg, state)) redraw = true ; + break; + + //--------------------------------------------- + case EVID_KEY: // Key events + patBacklight(state); + if (evKey(&msg, state)) redraw = true; + break; + + //--------------------------------------------- + case EVID_NONE: + break; + + //--------------------------------------------- + default: // Unknown event + WARN("Unknown message.ID [%d]", msg.id); + break; + } + + // *** Update the GUI screen via the viewport *** + if (redraw) view_port_update(vpp) ; + + // *** Try to release the plugin state variables *** + if ( !release_mutex(&mutex, state) ) { + ERROR(wii_errs[(error = ERR_MUTEX_RELEASE)]); + goto bail; + } + } while (state->run); + + // ===== Game Over ===== + INFO("USER EXIT"); + +bail: + // 10. Release system notification queue + if (state->notify) { + furi_record_close(RECORD_NOTIFICATION); + state->notify = NULL; + } + + // 9. Stop the timer + if (state->timer) { + (void)furi_timer_stop(state->timer); + furi_timer_free(state->timer); + state->timer = NULL; + state->timerEn = false; + } + + // 8. Detach the viewport + gui_remove_view_port(gui, vpp); + + // 7. No need to unreqgister the callbacks + // ...they will go when the viewport is destroyed + + // 6. Destroy the viewport + if (vpp) { + view_port_enabled_set(vpp, false); + view_port_free(vpp); + vpp = NULL; + } + + // 5. Free the mutex + if (mutex.mutex) { + delete_mutex(&mutex); + mutex.mutex = NULL; + } + + // 4. Free up state pointer(s) + // none + + // 3. Free the plugin state variables + if (state) { + free(state); + state = NULL; + } + + // 2. Close the GUI + furi_record_close("gui"); + + // 1. Destroy the message queue + if (queue) { + furi_message_queue_free(queue); + queue = NULL; + } + + INFO("CLEAN EXIT ... Exit code: %d", error); + LEAVE; + return (int32_t)error; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal.h new file mode 100644 index 000000000..ac7ffddb1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal.h @@ -0,0 +1,97 @@ +#ifndef WII_ANAL_H_ +#define WII_ANAL_H_ + +#include // Core API +#include // GUI Input extensions +#include + +//----------------------------------------------------------------------------- ---------------------------------------- +// GUI scenes +// +typedef + enum scene { + SCENE_NONE = 0, + SCENE_SPLASH = 1, + SCENE_RIP = 2, + SCENE_WAIT = 3, + SCENE_DEBUG = 4, + SCENE_DUMP = 5, + SCENE_CLASSIC = 6, + SCENE_CLASSIC_N = 7, + SCENE_NUNCHUCK = 8, + SCENE_NUNCHUCK_ACC = 9, + } +scene_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +#include "wii_i2c.h" +#include "wii_ec.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +// A list of event IDs handled by this plugin +// +typedef + enum eventID { + EVID_NONE, + EVID_UNKNOWN, + + // A full list of events can be found with: `grep -r --color "void.*set_.*_callback" applications/gui/*` + // ...A free gift to you from the makers of well written code that conforms to a good coding standard + EVID_KEY, // keypad + EVID_TICK, // tick + EVID_WIIEC, // wii extension controller + } +eventID_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +// An item in the event message-queue +// +typedef + struct eventMsg { + eventID_t id; + union { + InputEvent input; // --> applications/input/input.h + wiiEcEvent_t wiiEc; // --> local + }; + } +eventMsg_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +// State variables for this plugin +// An instance of this is allocated on the heap, and the pointer is passed back to the OS +// Access to this memory is controlled by mutex +// +typedef + struct state { + bool run; // true : plugin is running + + bool timerEn; // controller scanning enabled + FuriTimer* timer; // the timer + uint32_t timerHz; // system ticks per second + int fps; // poll/refresh [frames]-per-second + + int cnvW; // canvas width + int cnvH; // canvas height + scene_t scene; // current scene + scene_t scenePrev; // previous scene + scene_t scenePegg; // previous scene for easter eggs + int flash; // flash counter (flashing icons) + + int hold; // hold type: {-1=tough-peak, 0=none, +1=peak-hold} + ecCalib_t calib; // Software calibration mode + + bool pause; // Accelerometer animation pause + bool apause; // Accelerometer animation auto-pause + + NotificationApp* notify; // OS nitifcation queue (for patting the backlight watchdog timer) + + wiiEC_t ec; // Extension Controller details + } +state_t; + +//============================================================================= ======================================== +// Function prototypes +// +void timerEn (state_t* state, bool on) ; + +#endif //WII_ANAL_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_ec.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_ec.c new file mode 100644 index 000000000..0bfa47362 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_ec.c @@ -0,0 +1,97 @@ +#include +#include + +#include "wii_anal.h" +#include "wii_anal_lcd.h" +#include "wii_anal_keys.h" + +//+============================================================================ ======================================== +// Handle Wii Extension Controller events +// +bool evWiiEC (const eventMsg_t* const msg, state_t* const state) +{ + bool redraw = false; + +# if LOG_LEVEL >= 4 + { + const char* s = NULL; + switch (msg->wiiEc.type) { + case WIIEC_NONE: s = "Error"; break ; + case WIIEC_CONN: s = "Connect"; break ; + case WIIEC_DISCONN: s = "Disconnect"; break ; + case WIIEC_PRESS: s = "Press"; break ; + case WIIEC_RELEASE: s = "Release"; break ; + case WIIEC_ANALOG: s = "Analog"; break ; + case WIIEC_ACCEL: s = "Accel"; break ; + default: s = "Bug"; break ; + } + INFO("WIIP : %s '%c' = %d", s, (isprint((int)msg->wiiEc.in) ? msg->wiiEc.in : '_'), msg->wiiEc.val); + if ((msg->wiiEc.type == WIIEC_CONN) || (msg->wiiEc.type == WIIEC_DISCONN)) + INFO("...%d=\"%s\"", msg->wiiEc.val, ecId[msg->wiiEc.val].name); + } +# endif + + switch (msg->wiiEc.type) { + case WIIEC_CONN: + patBacklight(state); + state->hold = 0; + state->calib = CAL_TRACK; + sceneSet(state, ecId[msg->wiiEc.val].scene); + redraw = true ; + +#if 1 // Workaround for Classic Controller Pro, which shows 00's for Factory Calibration Data!? + if (state->ec.pidx == PID_CLASSIC_PRO) { + // Simulate a Long-OK keypress, to start Software Calibration mode + eventMsg_t msg = { +// .id = EVID_KEY, + .input.type = InputTypeLong, + .input.key = InputKeyOk + }; + key_calib(&msg, state); + } +#endif + break; + + case WIIEC_DISCONN: + patBacklight(state); + sceneSet(state, SCENE_WAIT); + redraw = true; + break; + + case WIIEC_PRESS: + if (state->scene == SCENE_NUNCHUCK_ACC) switch (msg->wiiEc.in) { + case 'z': // un-pause + state->pause = !state->pause; + break; + case 'c': // toggle auto-pause + state->pause = false; + state->apause = !state->apause; + break; + default: break ; + } + +#if 1 //! factory calibration method not known for classic triggers - this will set the digital switch point + if ((state->ec.pidx == PID_CLASSIC) || (state->ec.pidx == PID_CLASSIC_PRO)) { + if (msg->wiiEc.in == 'l') state->ec.calS.classic[2].trgZL = msg->wiiEc.val ; + if (msg->wiiEc.in == 'r') state->ec.calS.classic[2].trgZR = msg->wiiEc.val ; + } +#endif + __attribute__ ((fallthrough)); + + case WIIEC_RELEASE: + patBacklight(state); + redraw = true; + break; + + case WIIEC_ANALOG: + case WIIEC_ACCEL: + ecCalibrate(&state->ec, state->calib); + redraw = true; + break; + + default: + break; + } + + return redraw; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_ec.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_ec.h new file mode 100644 index 000000000..886b60281 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_ec.h @@ -0,0 +1,14 @@ +#ifndef WII_ANAL_EC_H_ +#define WII_ANAL_EC_H_ + +#include + +//============================================================================= ======================================== +// Function prototypes +// +typedef struct eventMsg eventMsg_t ; +typedef struct state state_t ; + +bool evWiiEC (const eventMsg_t* const msg, state_t* const state) ; + +#endif //WII_ANAL_EC_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_keys.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_keys.c new file mode 100644 index 000000000..3d2fae14a --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_keys.c @@ -0,0 +1,297 @@ +#include + +#include "bc_logging.h" + +#include "wii_anal.h" + +//+============================================================================ ======================================== +// Stop Calibration mode +// +static +void calStop (state_t* const state) +{ + state->hold = 0; // stop calibration mode + state->calib &= ~(CAL_RANGE | CAL_NOTJOY); // ... +} + +//+============================================================================ ======================================== +// Change to another scene +// +void sceneSet (state_t* const state, const scene_t scene) +{ + calStop(state); // Stop software calibration + state->scenePrev = state->scene; // Remember where we came from + state->scene = scene; // Go to new scene + INFO("Scene : %d -> %d", state->scenePrev, state->scene); +} + +//+============================================================================ ======================================== +// Change to an easter egg scene +// +static +void sceneSetEgg (state_t* const state, const scene_t scene) +{ + calStop(state); // Stop software calibration + state->scenePegg = state->scene; // Remember where we came from + state->scene = scene; // Go to new scene + INFO("Scene* : %d => %d", state->scenePegg, state->scene); +} + +//+============================================================================ ======================================== +// Several EC screens have 'peak-hold' and 'calibration' features +// Enabling peak-hold on screen with no peak meters will have no effect +// So, to avoid code duplication... +// +bool key_calib (const eventMsg_t* const msg, state_t* const state) +{ + int used = false; // assume key is NOT-handled + + switch (msg->input.type) { + case InputTypeShort: //# input.key) { + case InputKeyUp: //# hold = (state->hold == +1) ? 0 : +1 ; // toggle peak hold + used = true; + break; + + case InputKeyDown: //# hold = (state->hold == -1) ? 0 : -1 ; // toggle trough hold + used = true; + break; + + case InputKeyOk: //# calib & CAL_RANGE) calStop(state) ; // STOP softare calibration + else ecCalibrate(&state->ec, CAL_CENTRE) ; // perform centre calibration + used = true; + break; + + default: break ; + } + break; + + case InputTypeLong: //# >! After INPUT_LONG_PRESS interval, asynch to InputTypeRelease + switch (msg->input.key) { + case InputKeyOk: //# >O [ LONG-OK ] + ecCalibrate(&state->ec, CAL_RESET | CAL_CENTRE); // START software calibration + state->hold = 0; + state->calib |= CAL_RANGE; + state->flash = 8; // start with flash ON + used = true; + break; + + default: break ; + } + break; + + default: break ; + } + + return used; +} + +//+============================================================================ ======================================== +// WAIT screen +// +static inline +bool wait_key (const eventMsg_t* const msg, state_t* const state) +{ + int used = false; // assume key is NOT-handled + + if (msg->input.type == InputTypeShort) { + switch (msg->input.key) { + case InputKeyLeft: //# run = false; + used = true; + break; + + default: break ; + } + } + + return used; +} + +//+============================================================================ ======================================== +// DEBUG screen +// +static inline +bool debug_key (const eventMsg_t* const msg, state_t* const state) +{ + int used = false; // assume key is NOT-handled + + switch (msg->input.type) { + case InputTypeShort: //# input.key) { + case InputKeyUp: { //# ec, NULL); // Initialise the controller //! NULL = no encryption + (void)init; // in case INFO is optimised out + INFO("%s : %s", __func__, (init ? "init OK" : "init fail")); + used = true; + break; + } + + case InputKeyOk: //# ec) == 0) { // Read the controller + INFO( "%s : joy: {%02X,%02X,%02X,%02X,%02X,%02X}", __func__, + state->ec.joy[0], state->ec.joy[1], state->ec.joy[2], + state->ec.joy[3], state->ec.joy[4], state->ec.joy[5] ); + } + used = true; + break; + + case InputKeyDown: //# scenePrev); + used = true; + break; + + case InputKeyBack: //# run = false; + used = true; + break; + + default: break ; //# input.key == InputKeyBack) && (state->scenePrev == SCENE_NONE)) state->run = false ; + + // ANY-other-KEY press + if (msg->input.type == InputTypeShort) { + timerEn(state, true); // Restart the timer + state->scene = state->scenePegg; + } + + return true; +} + + +//+============================================================================ ======================================== +// "_pre" allows the plugin to use the key before the active scene gets a chance +// +static inline +bool key_pre (const eventMsg_t* const msg, state_t* const state) +{ + (void)msg; + (void)state; + + return false; +} + +//+============================================================================ ======================================== +// "_post" allows the plugin to use a key if it was not used by the active scene +// +static inline +bool key_post (const eventMsg_t* const msg, state_t* const state) +{ + int used = false; // assume key is NOT-handled + + if (msg->input.key == InputKeyBack) { + if (msg->input.type == InputTypeShort) { //# ec.init = false; // reset/disconnect the controller + sceneSet(state, SCENE_WAIT); + used = true; + + } else if (msg->input.type == InputTypeLong) { //# >B [LONG-BACK] + state->run = false; // Signal the plugin to exit + used = true; + } + } + + // Easter eggs + switch (state->scene) { + case SCENE_SPLASH: // Scenes that do NOT offer Easter eggs + case SCENE_RIP: + case SCENE_DEBUG: + break; + default: + if (msg->input.type == InputTypeLong) { + switch (msg->input.key) { + case InputKeyDown: //# >D [LONG-DOWN] + timerEn(state, false); // Stop the timer + sceneSetEgg(state, SCENE_DEBUG); + used = true; + break; + + case InputKeyLeft: //# >L [ LONG-LEFT ] + timerEn(state, false); // Stop the timer + sceneSetEgg(state, SCENE_SPLASH); + used = true; + break; + + case InputKeyUp: //# >U [ LONG-UP ] + timerEn(state, false); // Stop the timer + sceneSetEgg(state, SCENE_RIP); + used = true; + break; + + default: break ; + } + } + break; + } + + return used; +} + +//+============================================================================ ======================================== +// Handle a key press event +// +bool evKey (const eventMsg_t* const msg, state_t* const state) +{ + furi_assert(msg); + furi_assert(state); + + bool used = key_pre(msg, state); + + if (!used) switch (state->scene) { + case SCENE_SPLASH: //... + case SCENE_RIP: used = splash_key(msg, state); break ; + + case SCENE_WAIT: used = wait_key(msg, state); break ; + case SCENE_DEBUG: used = debug_key(msg, state); break ; + + default: + if (state->ec.pidx >= PID_ERROR) { + ERROR("%s : bad PID = %d", __func__, state->ec.pidx); + } else { + if ((state->scene == SCENE_DUMP) || !ecId[state->ec.pidx].keys) + ecId[PID_UNKNOWN].keys(msg, state); + else + ecId[state->ec.pidx].keys(msg, state); + } + break; + + case SCENE_NONE: break; + } + + if (!used) used = key_post(msg, state) ; + + return used; +} + diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_keys.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_keys.h new file mode 100644 index 000000000..0ebbd5e8e --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_keys.h @@ -0,0 +1,16 @@ +#ifndef WII_ANAL_KEYS_H_ +#define WII_ANAL_KEYS_H_ + +//============================================================================= ======================================== +// Function prototypes +// +#include // bool +typedef struct eventMsg eventMsg_t ; +typedef struct state state_t ; +typedef enum scene scene_t ; + +void sceneSet (state_t* const state, const scene_t scene) ; +bool key_calib (const eventMsg_t* const msg, state_t* const state) ; +bool evKey (const eventMsg_t* const msg, state_t* const state) ; + +#endif //WII_ANAL_KEYS_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_lcd.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_lcd.c new file mode 100644 index 000000000..d031bc120 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_lcd.c @@ -0,0 +1,223 @@ +#include "wii_anal.h" +#include "gfx/images.h" // Images + +//----------------------------------------------------------------------------- ---------------------------------------- +// A couple of monospaced hex fonts +// +const image_t* img_6x8[16] = { + &img_6x8_0, &img_6x8_1, &img_6x8_2, &img_6x8_3, &img_6x8_4, &img_6x8_5, &img_6x8_6, &img_6x8_7, + &img_6x8_8, &img_6x8_9, &img_6x8_A, &img_6x8_B, &img_6x8_C, &img_6x8_D, &img_6x8_E, &img_6x8_F, +}; + +const image_t* img_5x7[16] = { + &img_5x7_0, &img_5x7_1, &img_5x7_2, &img_5x7_3, &img_5x7_4, &img_5x7_5, &img_5x7_6, &img_5x7_7, + &img_5x7_8, &img_5x7_9, &img_5x7_A, &img_5x7_B, &img_5x7_C, &img_5x7_D, &img_5x7_E, &img_5x7_F, +}; + +//+============================================================================ ======================================== +// void backlightOn (void) +// { +// // Acquire a handle for the system notification queue +// // Do this ONCE ... at plugin startup +// NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); +// +// // Pat the backlight watchdog +// // Send the (predefined) message sequence {backlight_on, end} +// // --> applications/notification/*.c +// notification_message(notifications, &sequence_display_backlight_on); +// +// // Release the handle for the system notification queue +// // Do this ONCE ... at plugin quit +// furi_record_close(RECORD_NOTIFICATION); +// } +void patBacklight (state_t* state) +{ + notification_message(state->notify, &sequence_display_backlight_on); +} + +//============================================================================= ======================================== +// Show a hex number in an inverted box (for ananlogue readings) +// +void showHex ( Canvas* const canvas, uint8_t x, uint8_t y, + const uint32_t val, const uint8_t cnt, const int b ) +{ + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, x++,y++, 1 +(cnt *(6 +1)), 10); + + // thicken border + if (b == 2) canvas_draw_frame(canvas, x-2,y-2, 1 +(cnt *(6 +1))+2, 10+2); + + for (int i = (cnt -1) *4; i >= 0; i -= 4, x += 6+1) + show(canvas, x,y, img_6x8[(val >>i) &0xF], SHOW_SET_WHT) ; +} + +//============================================================================= ======================================== +// Show the up/down "peak hold" controls in the bottom right +// +void showPeakHold (state_t* const state, Canvas* const canvas, const int hold) +{ + switch (hold) { + case 0: + show(canvas, 119,51, &img_key_U, SHOW_CLR_BLK); + show(canvas, 119,56, &img_key_D, SHOW_CLR_BLK); + break; + case +1: + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 120,52, 7,6); + show(canvas, 119,51, &img_key_U, SHOW_CLR_WHT); + show(canvas, 119,56, &img_key_D, SHOW_CLR_BLK); + break; + case -1: + show(canvas, 119,51, &img_key_U, SHOW_CLR_BLK); + canvas_draw_box(canvas, 120,57, 7,6); + show(canvas, 119,56, &img_key_D, SHOW_CLR_WHT); + break; + default: + break; + } + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, 119,51, 9,13); + + // calibration indicator + show( canvas, 108,55, + ((state->calib & CAL_RANGE) && (++state->flash &8)) ? &img_key_OKi : &img_key_OK, + SHOW_SET_BLK ); +} + +//============================================================================= ======================================== +// This code performs a FULL calibration on the device EVERY time it draws a joystick +//...This is NOT a good way forward for anything other than a test tool. +// +// Realistically you would do all the maths when the controller is connected +// or, if you prefer (and it IS a good thing), have a "calibrate controller" menu option +// ...and then just use a lookup table, or trivial formual +// +// THIS algorithm chops the joystick in to one of 9 zones +// Eg. {FullLeft, Left3, Left2, Left1, Middle, Right1, Right2, Right3, FullRight} +// FullLeft and FullRight have a deadzone of N [qv. xDead] ..a total of N+1 positions +// Middle has a deadzone of N EACH WAY ...a total of 2N+1 positions +// +// If the remaining range does not divide evenly in to three zones, +// the first remainder is added to zone3, +// and the second remainder (if there is one) is added to zone2 +// ...giving finer control near the centre of the joystick +// +// The value of the deadzone is based on the number of bits in the +// joystcik {x,y} values - the larger the range, the larger the deadzone. +// +// 03 15 29 +// |<<| Calibration points |==| |>>| +// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F +// |---| |________________________| |------| |______________________________| |---| +// |r=2| | range = 9 | | r=3 | | range = 11 | |r=2| +// Zones: |-4 | |-3 |-2 |-1 | |0 | |+1 |+2 |+3 | |+4 | +// +// This is not "the right way to do it" ...this is "one way to do it" +// Consider you application, and what the user is trying to achieve +// Aim a gun - probably need to be more accurate +// Turn and object - this is probably good enough +// Start slowly & pick up speed - how about a log or sine curve? +// +void showJoy ( Canvas* const canvas, const uint8_t x, const uint8_t y, // x,y is the CENTRE of the Joystick + const uint8_t xMin, const uint8_t xMid, const uint8_t xMax, + const uint8_t yMin, const uint8_t yMid, const uint8_t yMax, + const uint8_t xPos, const uint8_t yPos, const uint8_t bits ) +{ + int xOff = 0; // final offset of joystick hat image + int yOff = 0; + + int xDead = (bits < 7) ? (1<<0) : (1<<3); // dead zone (centre & limits) + int yDead = xDead; + + // This code is NOT optimised ...and it's still barely readable! + if ((xPos >= (xMid -xDead)) && (xPos <= (xMid +xDead))) xOff = 0 ; // centre [most likely] + else if (xPos <= (xMin +xDead)) xOff = -4 ; // full left + else if (xPos >= (xMax -xDead)) xOff = +4 ; // full right + else if (xPos < (xMid -xDead)) { // part left + // very much hard-coded for 3 interim positions + int lo = (xMin +xDead) +1; // lowest position + int hi = (xMid -xDead) -1; // highest position + + // this is the only duplicated bit of code + int range = (hi -lo) +1; // range covered + int div = range /3; // each division (base amount, eg. 17/3==5) + int rem = range -(div *3); // remainder (ie. range%3) + +// int hi1 = hi; // lowest value for zone #-1 +// int lo1 = hi1 -div +1; // highest value for zone #-1 +// int hi2 = lo1 -1; // lowest value for zone #-2 +// int lo2 = hi2 -div +1 -(rem==2); // highest value for zone #-2 expand out remainder +// int hi3 = lo2 -1; // lowest value for zone #-3 +// int lo3 = hi3 -div +1 -(rem>=1); // highest value for zone #-3 expand out remainder + + int lo1 = hi -div +1; // (in brevity) + int hi3 = hi -div -div -(rem==2); // ... + + if (xPos <= hi3) xOff = -3 ; // zone #-3 + else if (xPos >= lo1) xOff = -1 ; // zone #-1 + else xOff = -2 ; // zone #-2 + + } else /*if (xPos > (xMid +xDead))*/ { // part right + // very much hard-coded for 3 interim positions + int lo = (xMid +xDead) +1; // lowest position + int hi = (xMax -xDead) -1; // highest position + + int range = (hi -lo) +1; // range covered + int div = range /3; // each division (base amount, eg. 17/3==5) + int rem = range -(div *3); // remainder (ie. range%3) + +// int lo1 = lo; // lowest value for zone #+1 +// int hi1 = lo +div -1; // highest value for zone #+1 +// int lo2 = hi1 +1; // lowest value for zone #+2 +// int hi2 = lo2 +div -1 +(rem==2); // highest value for zone #+2 expand out remainder +// int lo3 = hi2 +1; // lowest value for zone #+3 +// int hi3 = lo3 +div -1 +(rem>=1); // highest value for zone #+3 expand out remainder + + int hi1 = lo +div -1; // (in brevity) + int lo3 = lo +div +div +(rem==2); // ... + + if (xPos <= hi1) xOff = 1 ; // zone #1 + else if (xPos >= lo3) xOff = 3 ; // zone #3 + else xOff = 2 ; // zone #2 + } + + // All this to print a 3x3 square (in the right place) - LOL! + if ((yPos >= (yMid -yDead)) && (yPos <= (yMid +yDead))) yOff = 0 ; // centre [most likely] + else if (yPos <= (yMin +yDead)) yOff = +4 ; // full down + else if (yPos >= (yMax -yDead)) yOff = -4 ; // full up + else if (yPos < (yMid -yDead)) { // part down + int lo = (yMin +yDead) +1; // lowest position + int hi = (yMid -yDead) -1; // highest position + + int range = (hi -lo) +1; // range covered + int div = range /3; // each division (base amount, eg. 17/3==5) + int rem = range -(div *3); // remainder (ie. range%3) + + int lo1 = hi -div +1; // (in brevity) + int hi3 = hi -div -div -(rem==2); // ... + + if (yPos <= hi3) yOff = +3 ; // zone #3 + else if (yPos >= lo1) yOff = +1 ; // zone #1 + else yOff = +2 ; // zone #2 + + } else /*if (yPos > (yMid +yDead))*/ { // part up + int lo = (yMid +yDead) +1; // lowest position + int hi = (yMax -yDead) -1; // highest position + + int range = (hi -lo) +1; // range covered + int div = range /3; // each division (base amount, eg. 17/3==5) + int rem = range -(div *3); // remainder (ie. range%3) + + int hi1 = lo +div -1; // (in brevity) + int lo3 = lo +div +div +(rem==2); // ... + + if (yPos <= hi1) yOff = -1 ; // zone #-1 + else if (yPos >= lo3) yOff = -3 ; // zone #-3 + else yOff = -2 ; // zone #-2 + } + + show(canvas, x-(img_cc_Joy.w/2),y-(img_cc_Joy.h/2), &img_cc_Joy, SHOW_SET_BLK); + + // All ^that^ for v-this-v - LOL!! + canvas_draw_box(canvas, (x-1)+xOff,(y-1)+yOff, 3,3); +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_lcd.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_lcd.h new file mode 100644 index 000000000..5258c4de1 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_lcd.h @@ -0,0 +1,43 @@ +#ifndef WII_ANAL_LCD_H_ +#define WII_ANAL_LCD_H_ + +//----------------------------------------------------------------------------- ---------------------------------------- +// A couple of monospaced hex fonts +// +#include "gfx/images.h" + +extern const image_t* img_6x8[]; +extern const image_t* img_5x7[]; + +//============================================================================= ======================================== +// macros to draw only two sides of a box +// these are used for drawing the wires on the WAIT screen +// +#define BOX_TL(x1,y1,x2,y2) do { \ + canvas_draw_frame(canvas, x1,y1, x2-x1+1,2); \ + canvas_draw_frame(canvas, x1,y1+2, 2,y2-y1+1-2); \ +}while(0) + +#define BOX_BL(x1,y1,x2,y2) do { \ + canvas_draw_frame(canvas, x1,y2-1, x2-x1+1,2); \ + canvas_draw_frame(canvas, x1,y1, 2,y2-y1+1-2); \ +}while(0) + + +//============================================================================= ======================================== +// Function prototypes +// +void patBacklight (state_t* state) ; + +void showHex ( Canvas* const canvas, uint8_t x, uint8_t y, + const uint32_t val, const uint8_t cnt, const int b ) ; + +void showPeakHold (state_t* const state, Canvas* const canvas, const int hold) ; + +void showJoy ( Canvas* const canvas, const uint8_t x, const uint8_t y, // x,y is the CENTRE of the Joystick + const uint8_t xMin, const uint8_t xMid, const uint8_t xMax, + const uint8_t yMin, const uint8_t yMid, const uint8_t yMax, + const uint8_t xPos, const uint8_t yPos, const uint8_t bits ) ; + + +#endif //WII_ANAL_LCD_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_ver.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_ver.h new file mode 100644 index 000000000..88ed17d19 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_anal_ver.h @@ -0,0 +1,9 @@ +#ifndef WII_ANAL_VER_H_ +#define WII_ANAL_VER_H_ + +#include "gfx/images.h" + +#define VER_MAJ &img_3x5_1 +#define VER_MIN &img_3x5_0 + +#endif //WII_ANAL_VER_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec.c new file mode 100644 index 000000000..93b17c67c --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec.c @@ -0,0 +1,220 @@ +#include +#include // Core API + +#include "wii_anal.h" +#include "wii_i2c.h" +#include "wii_ec.h" +#include "bc_logging.h" + +#include "gfx/images.h" // Images +#include "wii_anal_lcd.h" // Drawing functions +#include "wii_anal_keys.h" // key mappings + +//----------------------------------------------------------------------------- ---------------------------------------- +// List of known perhipherals +// +// More perhipheral ID codes here: https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way +// +const ecId_t ecId[PID_CNT] = { + [PID_UNKNOWN ] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, "Unknown Perhipheral", SCENE_DUMP, + NULL, NULL, NULL, NULL, ec_show, ec_key}, + + // If you're wise, ONLY edit this bit + [PID_NUNCHUCK ] = { {0x00, 0x00, 0xA4, 0x20, 0x00, 0x00}, "Nunchuck", SCENE_NUNCHUCK, + NULL, nunchuck_decode, nunchuck_msg, nunchuck_calib, nunchuck_show, nunchuck_key }, + + [PID_NUNCHUCK_R2] = { {0xFF, 0x00, 0xA4, 0x20, 0x00, 0x00}, "Nunchuck (rev2)", SCENE_NUNCHUCK, + NULL, nunchuck_decode, nunchuck_msg, nunchuck_calib, nunchuck_show, nunchuck_key }, + + [PID_CLASSIC ] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x01}, "Classic Controller", SCENE_CLASSIC, + NULL, classic_decode, classic_msg, classic_calib, classic_show, classic_key }, + + [PID_CLASSIC_PRO] = { {0x01, 0x00, 0xA4, 0x20, 0x01, 0x01}, "Classic Controller Pro", SCENE_CLASSIC, + NULL, classic_decode, classic_msg, classic_calib, classic_show, classic_key }, + + [PID_BALANCE ] = { {0x00, 0x00, 0xA4, 0x20, 0x04, 0x02}, "Balance Board", SCENE_DUMP, + NULL, NULL, NULL, NULL, NULL, NULL }, + + [PID_GH_GUITAR ] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x03}, "Guitar Hero Guitar", SCENE_DUMP, + NULL, NULL, NULL, NULL, NULL, NULL }, + + [PID_GH_DRUMS ] = { {0x01, 0x00, 0xA4, 0x20, 0x01, 0x03}, "Guitar Hero World Tour Drums", SCENE_DUMP, + NULL, NULL, NULL, NULL, NULL, NULL }, + + [PID_TURNTABLE ] = { {0x03, 0x00, 0xA4, 0x20, 0x01, 0x03}, "DJ Hero Turntable", SCENE_DUMP, + NULL, NULL, NULL, NULL, NULL, NULL }, + + [PID_TAIKO_DRUMS] = { {0x00, 0x00, 0xA4, 0x20, 0x01, 0x11}, "Taiko Drum Controller)", SCENE_DUMP, + NULL, NULL, NULL, NULL, NULL, NULL }, // Taiko no Tatsujin TaTaCon (Drum controller) + + [PID_UDRAW ] = { {0xFF, 0x00, 0xA4, 0x20, 0x00, 0x13}, "uDraw Tablet", SCENE_DUMP, + udraw_init, NULL, NULL, NULL, NULL, NULL }, //! same as drawsome? + // ----- + + [PID_ERROR ] = { {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, "Read Error", SCENE_NONE, + NULL, NULL, NULL, NULL, NULL, NULL }, + + [PID_NULL ] = { {0}, NULL, SCENE_NONE, NULL, NULL, NULL, NULL, NULL, NULL } // last entry +}; + +//+============================================================================ ======================================== +void ecDecode (wiiEC_t* pec) +{ + if (ecId[pec->pidx].decode) ecId[pec->pidx].decode(pec) ; +} + +//+============================================================================ ======================================== +void ecCalibrate (wiiEC_t* const pec, ecCalib_t c) +{ + if (ecId[pec->pidx].calib) ecId[pec->pidx].calib(pec, c) ; +} + +//+============================================================================ ======================================== +void ecPoll (wiiEC_t* const pec, FuriMessageQueue* const queue) +{ + ENTER; + furi_assert(queue); + + if (!pec->init) { + // Attempt to initialise + if (ecInit(pec, NULL)) { //! need a way to auto-start with encryption enabled + eventMsg_t msg = { + .id = EVID_WIIEC, + .wiiEc = { + .type = WIIEC_CONN, + .in = '<', + .val = pec->pidx + } + }; + furi_message_queue_put(queue, &msg, 0); + } + + } else { + // Attempt to read + switch (ecRead(pec)) { + case 2: { // device gone + eventMsg_t msg = { + .id = EVID_WIIEC, + .wiiEc = { + .type = WIIEC_DISCONN, + .in = '>', + .val = pec->pidx + } + }; + furi_message_queue_put(queue, &msg, 0); + break; + } + + case 0: { // read OK + void (*fn)(wiiEC_t*, FuriMessageQueue*) = ecId[pec->pidx].check; + if (fn) fn(pec, queue); + break; + } + + case 3: // read fail + // this is probably temporary just ignore it + break; + + default: // bug: unknown + case 1: // bug: not initialised - should never happen + ERROR("%s : read bug", __func__); + break; + } + } + + LEAVE; + return; +} + +//+============================================================================ ======================================== +// This is the screen drawn for an unknown controller +// It is also available by pressing LEFT (at least once) on a "known controller" screen +// +void ec_show (Canvas* const canvas, state_t* const state) +{ + wiiEC_t* pec = &state->ec; + int h = 11; // line height + int x = 1; // (initial) offset for bits + int y = -h; // previous y value + int yb = 0; // y for bit patterns + int c2 = 17; // column 2 + + // Headings + canvas_set_font(canvas, FontSecondary); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_str_aligned(canvas, 0 ,0, AlignLeft, AlignTop, "SID:"); + canvas_draw_str_aligned(canvas, c2,0, AlignLeft, AlignTop, pec->sid); + + canvas_draw_str_aligned(canvas, 0 ,11, AlignLeft, AlignTop, "PID:"); + canvas_draw_str_aligned(canvas, 0 ,22, AlignLeft, AlignTop, "Cal:"); + + // PID + x = c2; + for (int i = 0; i < 6; i++) { + show(canvas, x,11, img_5x7[pec->pid[i]>>4], SHOW_SET_BLK); + x += 5+1; + show(canvas, x,11, img_5x7[pec->pid[i]&0xF], SHOW_SET_BLK); + x += 5+1+2; + } + + // Calibrations data + y = 11; + for (int j = 0; j <= 8; j += 8) { + x = c2; + y += 11; + for (int i = 0; i < 8; i++) { + show(canvas, x,y, img_5x7[pec->calF[i+j]>>4], SHOW_SET_BLK); + x += 5+1; + show(canvas, x,y, img_5x7[pec->calF[i+j]&0xF], SHOW_SET_BLK); + x += 5+1+2; + } + } + + // Reading + x = 1; + y++; + yb = (y+=h) +h +2; + + canvas_draw_line(canvas, x,y-1, x,yb+4); + x += 2; + + for (int i = 0; i < JOY_LEN; i++) { + show(canvas, x+ 1,y, img_6x8[pec->joy[i]>>4], SHOW_SET_BLK); + show(canvas, x+11,y, img_6x8[pec->joy[i]&0xF], SHOW_SET_BLK); + + // bits + for (int m = 0x80; m; m >>= 1) { + x += 2 * !!(m & 0x08) ; // nybble step + canvas_draw_box(canvas, x,yb +(2*!(pec->joy[i] & m)), 2,2) ; + x += 2; // bit step + } + + // byte step + x += 1; + canvas_draw_line(canvas, x,y-1, x,yb+4); + x += 2; + } + + // Scene navigation + if (state->scenePrev != SCENE_WAIT) + show(canvas, 120,0, &img_key_R, SHOW_SET_BLK); +} + +//+============================================================================ ======================================== +// The DUMP screen is +// +bool ec_key (const eventMsg_t* const msg, state_t* const state) +{ + int used = false; // assume key is NOT-handled + + if (state->scenePrev != SCENE_WAIT) { + //# input.type == InputTypeShort) && (msg->input.key == InputKeyRight)) { + sceneSet(state, state->scenePrev); + used = true; + } + } + + return used; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec.h new file mode 100644 index 000000000..e8745ba13 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec.h @@ -0,0 +1,177 @@ +#ifndef WII_EC_H_ +#define WII_EC_H_ + +#include + +#include + +#include "wii_ec_nunchuck.h" +#include "wii_ec_classic.h" +#include "wii_ec_udraw.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +// Crypto key (PSK), base register : {0x40..0x4F}[2][8] +#define ENC_LEN (2*8) + +// Controller State data, base register : {0x00..0x05}[6] +#define JOY_LEN (6) + +// Calibration data, base register : {0x20..0x2F}[16] +#define CAL_LEN (16) + +// Controller ID, base register : {0xFA..0xFF}[6] +#define PID_LEN (6) + +//----------------------------------------------------------------------------- ---------------------------------------- +// Perhipheral specific parameters union +// +typedef + union ecDec { + ecDecNunchuck_t nunchuck; + ecDecClassic_t classic; + } +ecDec_t; + +//----------------------------------------------------------------------------- +typedef + union ecCal { + // 0=lowest seen ; 1=min ; 2=mid ; 3=max ; 4=highest seen + ecCalNunchuck_t nunchuck[5]; + ecCalClassic_t classic[5]; + } +ecCal_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +// Wii Extension Controller events +// +typedef + enum wiiEcEventType { + WIIEC_NONE, + WIIEC_CONN, // Connect + WIIEC_DISCONN, // Disconnect + WIIEC_PRESS, // Press button + WIIEC_RELEASE, // Release button + WIIEC_ANALOG, // Analogue change (Joystick/Trigger) + WIIEC_ACCEL, // Accelerometer change + } +wiiEcEventType_t; + +//----------------------------------------------------------------------------- +typedef + struct wiiEcEvent { + wiiEcEventType_t type; // event type + char in; // input (see device specific options) + uint32_t val; // new value - meaningless for digital button presses + } +wiiEcEvent_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +// Known perhipheral types +// +typedef + enum ecPid { + PID_UNKNOWN = 0, + PID_FIRST = 1, + PID_NUNCHUCK = PID_FIRST, + + // If you're wise, ONLY edit this section + PID_NUNCHUCK_R2, + PID_CLASSIC, + PID_CLASSIC_PRO, + PID_BALANCE, + PID_GH_GUITAR, + PID_GH_DRUMS, + PID_TURNTABLE, + PID_TAIKO_DRUMS, + PID_UDRAW, //! same as drawsome? + // ----- + + PID_ERROR, + PID_NULL, + PID_CNT, + } +ecPid_t; + +//----------------------------------------------------------------------------- +// Calibration strategies +// +typedef + enum ecCalib { + CAL_FACTORY = 0x01, // (re)set to factory defaults + CAL_TRACK = 0x02, // track maximum and minimum values seen + CAL_RESET = 0x04, // initialise ready for software calibration + CAL_RANGE = 0x08, // perform software calibration step + CAL_CENTRE = 0x10, // reset centre point of joystick + CAL_NOTJOY = 0x20, // do NOT calibrate the joystick + } +ecCalib_t; + +//----------------------------------------------------------------------------- +// ecId table entry +// +typedef + struct ecId { + uint8_t id[6]; // 6 byte ID string returned by Extension Controller + char* name; // Friendly name + scene_t scene; // Default scene + bool (*init)(wiiEC_t*); // Additional initialisation code + void (*decode)(wiiEC_t*); // Decode function + void (*check)(wiiEC_t*, FuriMessageQueue*); // check (for action) function + void (*calib)(wiiEC_t*, ecCalib_t); // calibrate analogue controllers [SOFTWARE] + void (*show)(Canvas* const, state_t* const); // Draw scene + bool (*keys)(const eventMsg_t* const, state_t* const); // Interpret keys + } +ecId_t; + +//----------------------------------------------------------------------------- +// List of known perhipherals +// +// More perhipheral ID codes here: https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way +// +extern const ecId_t ecId[PID_CNT] ; + +//----------------------------------------------------------------------------- ---------------------------------------- +// Data pertaining to a single Perhipheral instance +// +typedef + struct wiiEC { + // Perhipheral state + bool init; // Initialised? + + uint8_t pid[PID_LEN]; // PID string - eg. {0x00, 0x00, 0xA4, 0x20, 0x00, 0x00} + ecPid_t pidx; // Index in to ecId table + const char* sid; // just for convenience + + bool encrypt; // encryption enabled? + uint8_t encKey[ENC_LEN]; // encryption key + + uint8_t calF[CAL_LEN]; // factory calibration data (not software) + uint8_t joy[JOY_LEN]; // Perhipheral raw data + + ecDec_t dec[2]; // device specific decode (two, so we can spot changes) + int decN; // which decode set is most recent {0, 1} + ecCal_t calS; // software calibration data + } +wiiEC_t; + +//----------------------------------------------------------------------------- ---------------------------------------- +// Function prototypes +// +// top level calls will work out which sub-function to call +// top level check() function will handle connect/disconnect messages +// + +#include // Canvas +typedef struct wiiEC wiiEC_t ; +typedef enum ecCalib ecCalib_t ; +typedef struct state state_t ; +typedef struct eventMsg eventMsg_t ; + +void ecDecode (wiiEC_t* const pec) ; +void ecPoll (wiiEC_t* const pec, FuriMessageQueue* const queue) ; +void ecCalibrate (wiiEC_t* const pec, ecCalib_t c) ; + +void ec_show ( Canvas* const canvas, state_t* const state) ; +bool ec_key (const eventMsg_t* const msg, state_t* const state) ; + +#endif //WII_EC_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_classic.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_classic.c new file mode 100644 index 000000000..91393ba07 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_classic.c @@ -0,0 +1,398 @@ +#include +#include // Core API + +#include "wii_anal.h" +#include "wii_ec.h" +#include "bc_logging.h" + +//#include "gfx/images.h" // Images +#include "wii_anal_lcd.h" // Drawing functions +#include "wii_anal_keys.h" // key mappings + +// ** If you want to see what this source code looks like with all the MACROs expanded +// ** grep -v '#include ' wii_i2c_classic.c | gcc -E -o /dev/stdout -xc - +# include "wii_ec_macros.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +// Classic Controller ... Classic Controller Pro is electronically the same +// +// ANA{l} ANA{r} +// BTN{l} BTN{L} BTN{R} BTN{r} +// ,--------. ,-, ,-, .--------, +// .----------------------------------------------------------. +// | | +// | BTN{W} BTN{x} | +// | BTN{A} BTN{D} BTN{-} BTN{h} BTN{+} BTN{y} BTN{a} | +// | BTN{S} BTN{b} | +// | | +// | ANA{y} ANA{Y} | +// | ANA{x} ANA{X} | +// | | +// `----------------------------------------------------------' + +//+============================================================================ ======================================== +// https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller +// I think a LOT of drugs went in to "designing" this layout +// ...And yes, the left-joystick has an extra 'bit' of precision! +// ...Also: trgZ{L|R} WILL continue to increase after btnZ{L|R} has gone active +// +void classic_decode (wiiEC_t* const pec) +{ + ecDecClassic_t* p = &pec->dec[(pec->decN = !pec->decN)].classic; + uint8_t* joy = pec->joy; + + p->trgZL = ((joy[2] >>2) &0x18) | ((joy[3] >>5) &0x07); // {5} + p->btnZL = !(joy[4] & 0x20); // !{1} + + p->trgZR = joy[3] & 0x1F; // {5} + p->btnZR = !(joy[4] & 0x02); // !{1} + + p->btnL = !(joy[5] & 0x80); // !{1} + p->btnR = !(joy[5] & 0x04); // !{1} + + p->padU = !(joy[5] & 0x01); // !{1} + p->padD = !(joy[4] & 0x40); // !{1} + p->padL = !(joy[5] & 0x02); // !{1} + p->padR = !(joy[4] & 0x80); // !{1} + + p->btnM = !(joy[4] & 0x10); // !{1} + p->btnH = !(joy[4] & 0x08); // !{1} + p->btnP = !(joy[4] & 0x04); // !{1} + + p->btnX = !(joy[5] & 0x08); // !{1} + p->btnY = !(joy[5] & 0x20); // !{1} + + p->btnA = !(joy[5] & 0x10); // !{1} + p->btnB = !(joy[5] & 0x40); // !{1} + + p->joyLX = joy[0] & 0x3F; // {6} + p->joyLY = joy[1] & 0x3F; // {6} + + p->joyRX = ((joy[0] >>3) &0x18) | ((joy[1] >>5) &0x06) | ((joy[2] >>7) &0x01); // {5} + p->joyRY = joy[2] & 0x1F; // {5} + + DEBUG( ">%d> ZL{%02X}%c, L:%c, R:%c, ZR{%02X}%c", pec->decN, + p->trgZL, (p->btnZL ? '#' : '.'), + (p->btnL ? '#' : '.'), + (p->btnR ? '#' : '.'), + p->trgZR, (p->btnZR ? '#' : '.') + ); + DEBUG( ">%d> D:{%c,%c,%c,%c}, H:{%c,%c,%c}, B:{%c,%c,%c,%c}", pec->decN, + (p->padU ? 'U' : '.'), (p->padD ? 'D' : '.'), (p->padL ? 'L' : '.'), (p->padR ? 'R' : '.'), + (p->btnM ? '-' : '.'), (p->btnH ? 'H' : '.'), (p->btnP ? '+' : '.'), + (p->btnX ? 'X' : '.'), (p->btnY ? 'Y' : '.'), (p->btnA ? 'A' : '.'), (p->btnB ? 'B' : '.') + ); + DEBUG( ">%d> JoyL{x:%02X, y:%02X}, JoyR{x:%02X, y:%02X}", pec->decN, + p->joyLX, p->joyLY, p->joyRX, p->joyRY + ); +} + +//+============================================================================ ======================================== +// Give each button a unique character identifier +// +void classic_msg (wiiEC_t* const pec, FuriMessageQueue* const queue) +{ + ecDecClassic_t* new = &pec->dec[pec->decN].classic; + ecDecClassic_t* old = &pec->dec[!pec->decN].classic; + + eventMsg_t msg = { + .id = EVID_WIIEC, + .wiiEc = { + .type = WIIEC_NONE, + .in = ' ', + .val = 0, + } + }; + + ANALOG(trgZL, 'l'); // FIVE bit value + ANABTN(btnZL, trgZL, 'l'); + + BUTTON(btnL, 'L'); + BUTTON(btnR, 'R'); + + ANALOG(trgZR, 'r'); // FIVE bit value + ANABTN(btnZR, trgZR, 'r'); + + BUTTON(padU, 'W'); + BUTTON(padL, 'A'); + BUTTON(padD, 'S'); + BUTTON(padR, 'D'); + + BUTTON(btnM, '-'); + BUTTON(btnH, 'h'); + BUTTON(btnP, '+'); + + BUTTON(btnX, 'x'); + BUTTON(btnY, 'y'); + BUTTON(btnA, 'a'); + BUTTON(btnB, 'b'); + + ANALOG(joyLX, 'x'); // SIX bit values + ANALOG(joyLY, 'y'); + + ANALOG(joyRX, 'X'); // FIVE bit values + ANALOG(joyRY, 'Y'); +} + +//+============================================================================ ======================================== +// https://web.archive.org/web/20090415045219/http://www.wiili.org/index.php/Wiimote/Extension_Controllers/Classic_Controller#Calibration_data +// +// Calibration data +// 0..2 left analog stick X axis {maximum, minimum, center} ... JoyL is 6bits, so >>2 to compare to readings +// 3..5 left analog stick Y axis {maximum, minimum, center} ... JoyL is 6bits, so >>2 to compare to readings +// 6..8 right analog stick X axis {maximum, minimum, center} ... JoyR is 5bits, so >>3 to compare to readings +// 9..11 right analog stick Y axis {maximum, minimum, center} ... JoyR is 5bits, so >>3 to compare to readings +// 12..15 somehow describe the shoulder {5bit} button values!? +// +void classic_calib (wiiEC_t* const pec, ecCalib_t c) +{ + ecDecClassic_t* src = &pec->dec[pec->decN].classic; // from input + ecCalClassic_t* dst = pec->calS.classic; // to calibration data + + if (c & CAL_RESET) { // initialise ready for software calibration + // LO is set to the MAXIMUM value (so it can be reduced) + // HI is set to ZERO (so it can be increased) + RESET_LO_HI(trgZL, 5); // 5bit value + RESET_LO_HI(trgZR, 5); // 5bit value + + RESET_LO_MID_HI(joyLX, 6); // 6bit value + RESET_LO_MID_HI(joyLY, 6); // 6bit value + + RESET_LO_MID_HI(joyRX, 5); // 5bit value + RESET_LO_MID_HI(joyRY, 5); // 5bit value + } + if (c & CAL_FACTORY) { // (re)set to factory defaults +//! strategy for factory calibration for classic controller [pro] triggers is (currently) unknown +//! FACTORY_LO( trgZL, pec->calF[12..15]); +//! FACTORY_MID(trgZL, pec->calF[12..15]); +//! FACTORY_HI( trgZL, pec->calF[12..15]); + +//! FACTORY_LO( trgZR, pec->calF[12..15]); +//! FACTORY_MID(trgZR, pec->calF[12..15]); +//! FACTORY_HI( trgZR, pec->calF[12..15]); + +#if 1 + FACTORY_LO(trgZL, 0x03); + FACTORY_LO(trgZR, 0x03); + + FACTORY_MID(trgZL, 0x1B); //! these will be set every time the digital switch changes to ON + FACTORY_MID(trgZR, 0x1B); +#endif + + FACTORY_LO( joyLX, pec->calF[ 1] >>2); + FACTORY_MID(joyLX, pec->calF[ 2] >>2); + FACTORY_HI( joyLX, pec->calF[ 0] >>2); + + FACTORY_LO( joyLY, pec->calF[ 4] >>2); + FACTORY_MID(joyLY, pec->calF[ 5] >>2); + FACTORY_HI( joyLY, pec->calF[ 3] >>2); + + FACTORY_LO( joyRX, pec->calF[ 7] >>3); + FACTORY_MID(joyRX, pec->calF[ 8] >>3); + FACTORY_HI( joyRX, pec->calF[ 6] >>3); + + FACTORY_LO( joyRY, pec->calF[10] >>3); + FACTORY_MID(joyRY, pec->calF[11] >>3); + FACTORY_HI( joyRY, pec->calF[ 9] >>3); + } + if (c & CAL_TRACK) { // track maximum and minimum values seen + TRACK_LO_HI(trgZL); + TRACK_LO_HI(trgZR); + + TRACK_LO_HI(joyLX); + TRACK_LO_HI(joyLY); + + TRACK_LO_HI(joyRX); + TRACK_LO_HI(joyRY); + } + if (c & CAL_RANGE) { // perform software calibration step + RANGE_LO_HI(trgZL); + RANGE_LO_HI(trgZR); + + RANGE_LO_HI(joyLX); + RANGE_LO_HI(joyLY); + + RANGE_LO_HI(joyRX); + RANGE_LO_HI(joyRY); + } + if (c & CAL_CENTRE) { // reset centre point of joystick + CENTRE(joyLX); + CENTRE(joyLY); + + CENTRE(joyRX); + CENTRE(joyRY); + } +} + +//+============================================================================ ======================================== +// bits that are common to both screens +// +static +void classic_show_ (Canvas* const canvas, state_t* const state) +{ + ecDecClassic_t* d = &state->ec.dec[state->ec.decN].classic; + ecCalClassic_t* js = state->ec.calS.classic; + + static const int dead = 1; // trigger deadzone + const image_t* img = NULL; // trigger image + + show(canvas, 6, 0, &img_cc_Main , SHOW_SET_BLK); + show(canvas, 62,53, &img_cc_Cable, SHOW_SET_BLK); + + // classic triggers + if (d->trgZL >= js[2].trgZL ) img = &img_cc_trg_L4; + else if (d->trgZL <= js[1].trgZL +dead) img = NULL; + else { + // copied from the joystick calibration code + int lo = js[1].trgZL +dead +1; + int hi = js[2].trgZL -1; + int range = hi -lo +1; + int div = range /3; // each division (base amount, eg. 17/3==5) + int rem = range -(div *3); // remainder (ie. range%3) + int hi1 = lo +div -1; // (in brevity) + int lo3 = lo +div +div +(rem==2); // ... + + if (d->trgZL <= hi1) img = &img_cc_trg_L1 ; // zone #1 + else if (d->trgZL >= lo3) img = &img_cc_trg_L3 ; // zone #3 + else img = &img_cc_trg_L2 ; // zone #2 + } + if (img) show(canvas, 22,1, img, SHOW_SET_BLK) ; + + if (d->trgZR >= js[2].trgZR ) img = &img_cc_trg_R4; + else if (d->trgZR <= js[1].trgZR +dead) img = NULL; + else { + // copied from the joystick calibration code + int lo = js[1].trgZR +dead +1; + int hi = js[2].trgZR -1; + int range = hi -lo +1; + int div = range /3; // each division (base amount, eg. 17/3==5) + int rem = range -(div *3); // remainder (ie. range%3) + int hi1 = lo +div -1; // (in brevity) + int lo3 = lo +div +div +(rem==2); // ... + + if (d->trgZR <= hi1) img = &img_cc_trg_R1 ; // zone #1 + else if (d->trgZR >= lo3) img = &img_cc_trg_R3 ; // zone #3 + else img = &img_cc_trg_R2 ; // zone #2 + } + if (img) show(canvas, 89,1, img, SHOW_SET_BLK) ; + + if (d->padU ) show(canvas, 27,16, &img_cc_pad_UD1, SHOW_ALL) ; + if (d->padL ) show(canvas, 20,23, &img_cc_pad_LR1, SHOW_ALL) ; + if (d->padD ) show(canvas, 27,28, &img_cc_pad_UD1, SHOW_ALL) ; + if (d->padR ) show(canvas, 32,23, &img_cc_pad_LR1, SHOW_ALL) ; + + if (d->btnX ) show(canvas, 96,16, &img_cc_btn_X1, SHOW_ALL) ; + if (d->btnY ) show(canvas, 85,23, &img_cc_btn_Y1, SHOW_ALL) ; + if (d->btnA ) show(canvas, 107,23, &img_cc_btn_A1, SHOW_ALL) ; + if (d->btnB ) show(canvas, 96,30, &img_cc_btn_B1, SHOW_ALL) ; + + canvas_set_color(canvas, ColorBlack); + if (d->btnL ) canvas_draw_box(canvas, 46,2, 5,4) ; + if (d->btnR ) canvas_draw_box(canvas, 77,2, 5,4) ; + + if (d->btnM ) canvas_draw_box(canvas, 54,24, 4,4) ; + if (d->btnH ) canvas_draw_box(canvas, 62,24, 4,4) ; + if (d->btnP ) canvas_draw_box(canvas, 70,24, 4,4) ; + + // Show joysticks + showJoy(canvas, 48,42, js[1].joyLX,js[2].joyLX, js[3].joyLX, + js[1].joyLY,js[2].joyLY, js[3].joyLY, d->joyLX,d->joyLY, 6); + showJoy(canvas, 78,42, js[1].joyRX,js[2].joyRX, js[3].joyRX, + js[1].joyRY,js[2].joyRY, js[3].joyRY, d->joyRX,d->joyRY, 5); + + show(canvas, 0,55, &img_key_L, SHOW_SET_BLK); +} + +//+============================================================================ ======================================== +static +void classic_showN (Canvas* const canvas, state_t* const state) +{ + ecCalClassic_t* c = (state->hold) ? &state->ec.calS.classic[(state->hold < 0) ? 0 : 4] + : (ecCalClassic_t*)(&state->ec.dec[state->ec.decN].classic) ; //! danger + + classic_show_(canvas, state); + + showHex(canvas, 0, 0, c->trgZL, 2,1); // 5bits + showHex(canvas, 113, 0, c->trgZR, 2,1); // 5bits + + showHex(canvas, 24,41, c->joyLX, 2,1); // 6bits + showHex(canvas, 41,54, c->joyLY, 2,1); // 6bits + + showHex(canvas, 88,41, c->joyRX, 2,1); // 5bits + showHex(canvas, 71,54, c->joyRY, 2,1); // 5bits + + showPeakHold(state, canvas, state->hold); // peak keys +} + +//+============================================================================ ======================================== +void classic_show (Canvas* const canvas, state_t* const state) +{ + // Classic controllers have TWO scenes + if (state->scene == SCENE_CLASSIC_N) return classic_showN(canvas, state) ; + + // Default scene + classic_show_(canvas, state); + show(canvas, 9,55, &img_key_R, SHOW_SET_BLK); + + show( canvas, 119,55, + ((state->calib & CAL_RANGE) && (++state->flash &8)) ? &img_key_OKi : &img_key_OK, + SHOW_SET_BLK ); +} + +//+============================================================================ ======================================== +static +bool classic_keyN (const eventMsg_t* const msg, state_t* const state) +{ + int used = false; // assume key is NOT-handled + + if ((msg->input.type == InputTypeShort) && (msg->input.key == InputKeyLeft)) { + sceneSet(state, SCENE_CLASSIC); + used = true; + } + + // Calibration keys + if (!used) used = key_calib(msg, state) ; + + return used; +} + +//+============================================================================ ======================================== +bool classic_key (const eventMsg_t* const msg, state_t* const state) +{ + // Classic controllers have TWO scenes + if (state->scene == SCENE_CLASSIC_N) return classic_keyN(msg, state) ; + + // Default scene + int used = false; // assume key is NOT-handled + + switch (msg->input.type) { + case InputTypeShort: //# input.key) { + case InputKeyUp: //# +#include + +//----------------------------------------------------------------------------- ---------------------------------------- +// Classic Controller ... Classic Controller Pro is electronically the same +// +// ANA{l} ANA{r} +// BTN{l} BTN{L} BTN{R} BTN{r} +// ,--------. ,-, ,-, .--------, +// .----------------------------------------------------------. +// | | +// | BTN{W} BTN{x} | +// | BTN{A} BTN{D} BTN{-} BTN{h} BTN{+} BTN{y} BTN{a} | +// | BTN{S} BTN{b} | +// | | +// | ANA{y} ANA{Y} | +// | ANA{x} ANA{X} | +// | | +// `----------------------------------------------------------' +// + +//----------------------------------------------------------------------------- ---------------------------------------- +// Controllers which have calibration must have their calibratable controls here +//! Is there a better way to get the start of the decode struct to match the calibration struct ? +#define CLASSIC_ANALOGUE \ + uint8_t trgZL, trgZR; /* ANA{l, l} lowercase=trigger 5bit values {5} */ \ + uint8_t joyLX, joyLY; /* ANA{x, y} left=lowercase 6bit values {6}<-- */ \ + uint8_t joyRX, joyRY; /* ANA{X, Y} 5bit values {5} */ + +//----------------------------------------------------------------------------- +// Calibratable controls +// +typedef + struct ecCalClassic + { + CLASSIC_ANALOGUE + } +ecCalClassic_t; + +//----------------------------------------------------------------------------- +// All controls +// +typedef + struct ecDecClassic + { + CLASSIC_ANALOGUE // MUST be first + + // Digital controls + bool btnZL, btnZR; // BTN{l, l} + + bool btnL, btnR; // BTN{L, R} upperrcase=shoulder + + bool padU, padL, padD, padR; // BTN{W, A, S, D} + + bool btnM, btnH, btnP; // BTN{-, h, +} + + bool btnX, btnY; // BTN{x, y} + bool btnA, btnB; // BTN{a, b} + + } +ecDecClassic_t; + +#undef CLASSIC_ANALOGUE + +//============================================================================= ======================================== +// Function prototypes +// +#include // Canvas +typedef struct wiiEC wiiEC_t ; +typedef enum ecCalib ecCalib_t ; +typedef struct state state_t ; +typedef struct eventMsg eventMsg_t ; + +void classic_decode (wiiEC_t* const pec) ; +void classic_msg (wiiEC_t* const pec, FuriMessageQueue* const queue) ; +void classic_calib (wiiEC_t* const pec, ecCalib_t c) ; + +void classic_show (Canvas* const canvas, state_t* const state) ; +bool classic_key (const eventMsg_t* const msg, state_t* const state) ; + +#endif //WII_EC_CLASSIC_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_macros.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_macros.h new file mode 100644 index 000000000..33daf944d --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_macros.h @@ -0,0 +1,84 @@ +#ifndef WII_EC_MACROS_H_ +#define WII_EC_MACROS_H_ + +//----------------------------------------------------------------------------- ---------------------------------------- +// CHECK MACROS +// +// I don't generally like this style of coding - it just (generally) makes things nightmarish to debug +// However, on this occasion I think it's a good choice (to make adding controllers LESS bug-prone) +// + +//if (furi_message_queue_get_count(queue) > 18) WARN("queue high %d", furi_message_queue_get_count(queue)); +#define MSGQ(lbl) do { \ + msg.wiiEc.in = lbl; \ + furi_message_queue_put(queue, &msg, 0); \ +}while(0) + + +// A 'standard' "button" is an independent SPST switch +// Eg. Nunchuck 'Z' button +// The "value" will always be 0 +#define BUTTON(btn,lbl) do { \ + if (new->btn != old->btn) { \ + msg.wiiEc.type = (new->btn) ? WIIEC_PRESS : WIIEC_RELEASE; \ + msg.wiiEc.val = 0; \ + MSGQ(lbl); \ + } \ +}while(0) + +// An "analogue button" is an SPST coupled with an ananlogue 'switch' +// Eg. The "bottom out" switches on the triggers of the classic controller +// The "value" will be the value of the associated analogue controller +#define ANABTN(btn,ana,lbl) do { \ + if (new->btn != old->btn) { \ + msg.wiiEc.type = (new->btn) ? WIIEC_PRESS : WIIEC_RELEASE; \ + msg.wiiEc.val = new->ana; \ + MSGQ(lbl); \ + } \ +}while(0) + +#define ANALOG(ana,lbl) do { \ + if (new->ana != old->ana) { \ + msg.wiiEc.type = WIIEC_ANALOG; \ + msg.wiiEc.val = new->ana; \ + MSGQ(lbl); \ + } \ +}while(0) + +#define ACCEL(acc,lbl) do { \ + if (new->acc != old->acc) { \ + msg.wiiEc.type = WIIEC_ACCEL; \ + msg.wiiEc.val = new->acc; \ + MSGQ(lbl); \ + } \ +}while(0) + +//----------------------------------------------------------------------------- ---------------------------------------- +// CALIBRATION MACROS +// +// Again ...I totally agree with anyone who says "MACRO coding" is (gernally) a poor choice of programming style +// But something about this code is making it soooo appealing +// +// ... v=variable, n=number +// +#define FACTORY_LO(v,n) do{ (dst[1]. v) = n; }while(0) +#define FACTORY_MID(v,n) do{ (dst[2]. v) = n; }while(0) +#define FACTORY_HI(v,n) do{ (dst[3]. v) = n; }while(0) + +#define TRACK_LO(v) do{ if ((src-> v) < (dst[0]. v)) (dst[0]. v) = (src-> v); }while(0) +#define TRACK_HI(v) do{ if ((src-> v) > (dst[4]. v)) (dst[4]. v) = (src-> v); }while(0) +#define TRACK_LO_HI(v) do{ TRACK_LO(v); TRACK_HI(v); }while(0) + +#define RESET_LO(v,b) do{ (dst[0]. v) = (dst[1]. v) = ((1<<(b))-1); }while(0) +#define RESET_HI(v) do{ (dst[4]. v) = (dst[3]. v) = 0; }while(0) +#define RESET_MID(v) do{ (dst[2]. v) = (src-> v); }while(0) +#define RESET_LO_HI(v,b) do{ RESET_LO(v,b); RESET_HI(v); }while(0) +#define RESET_LO_MID_HI(v,b) do{ RESET_LO(v,b); RESET_MID(v); RESET_HI(v); }while(0) + +#define RANGE_LO(v) do{ if ((src-> v) < (dst[1]. v)) (dst[1]. v) = (src-> v); }while(0) +#define RANGE_HI(v) do{ if ((src-> v) > (dst[3]. v)) (dst[3]. v) = (src-> v); }while(0) +#define RANGE_LO_HI(v) do{ RANGE_LO(v); RANGE_HI(v); }while(0) + +#define CENTRE(v) do{ (dst[2]. v) = (src-> v); } while(0) + +#endif //WII_EC_MACROS_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_nunchuck.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_nunchuck.c new file mode 100644 index 000000000..81c32a243 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_nunchuck.c @@ -0,0 +1,459 @@ +#include +#include // Core API + +#include "wii_anal.h" +#include "wii_i2c.h" +#include "bc_logging.h" + +#include "gfx/images.h" // Images +#include "wii_anal_lcd.h" // Drawing functions +#include "wii_anal_keys.h" // key mappings + +// ** If you want to see what this source code looks like with all the MACROs expanded +// ** grep -v '#include ' wii_ec_nunchuck.c | gcc -E -o /dev/stdout -xc - +# include "wii_ec_macros.h" + +//+============================================================================ ======================================== +// Standard Nunchuck : 2 buttons, 1 analogue joystick, 1 3-axis accelerometer +// +void nunchuck_decode (wiiEC_t* const pec) +{ + ecDecNunchuck_t* p = &pec->dec[(pec->decN = !pec->decN)].nunchuck; + uint8_t* joy = pec->joy; + + p->btnC = !(joy[5] & 0x02); // !{1} + p->btnZ = !(joy[5] & 0x01); // !{1} + + p->joyX = joy[0]; // {8} + p->joyY = joy[1]; // {8} + + p->accX = ((uint16_t)joy[2] << 2) | ((joy[5] >>2) & 0x03); // {10} + p->accY = ((uint16_t)joy[3] << 2) | ((joy[5] >>4) & 0x03); // {10} + p->accZ = ((uint16_t)joy[4] << 2) | ((joy[5] >>6) & 0x03); // {10} + + DEBUG(">%d> C:%c, Z:%c, Joy{x:%02X, y:%02X}, Acc{x:%03X, y:%03X, z:%03X}", pec->decN, + (p->btnC ? '#' : '.'), (p->btnZ ? '#' : '.'), + p->joyX, p->joyY, p->accX, p->accY, p->accZ + ); +} + +//+============================================================================ ======================================== +// Give each button a unique character identifier +// +void nunchuck_msg (wiiEC_t* const pec, FuriMessageQueue* const queue) +{ + ecDecNunchuck_t* new = &pec->dec[pec->decN].nunchuck; + ecDecNunchuck_t* old = &pec->dec[!pec->decN].nunchuck; + + eventMsg_t msg = { + .id = EVID_WIIEC, + .wiiEc = { + .type = WIIEC_NONE, + .in = ' ', + .val = 0, + } + }; + + BUTTON(btnC, 'c'); + BUTTON(btnZ, 'z'); + + ANALOG(joyX, 'x'); + ANALOG(joyY, 'y'); + + ACCEL(accX, 'x'); + ACCEL(accY, 'y'); + ACCEL(accZ, 'z'); +} + +//+============================================================================ ======================================== +// https://www.hackster.io/infusion/using-a-wii-nunchuk-with-arduino-597254#toc-5--read-actual-calibration-data-from-the-device-14 +// +void nunchuck_calib (wiiEC_t* const pec, ecCalib_t c) +{ + ecDecNunchuck_t* src = &pec->dec[pec->decN].nunchuck; // from input + ecCalNunchuck_t* dst = pec->calS.nunchuck; // to calibration data + + if (c & CAL_RESET) { // initialise ready for software calibration + // LO is set to the MAXIMUM value (so it can be reduced) + // HI is set to ZERO (so it can be increased) + RESET_LO_HI(accX, 10); // 10bit value + RESET_LO_HI(accY, 10); // 10bit value + RESET_LO_HI(accZ, 10); // 10bit value + + RESET_LO_HI(joyX, 8); // 8bit value + RESET_LO_HI(joyY, 8); // 8bit value + } + if (c & CAL_FACTORY) { // (re)set to factory defaults + //! "[4] LSB of Zero value of X,Y,Z axes" ...helpful! + //! ...Well, my test nunchuck has bits set in the bottom 6 bits, so let's guess ;) + + // No value available - annecdotal tests suggest 8 is reasonable + FACTORY_LO( accX, 8); + FACTORY_LO( accY, 8); + FACTORY_LO( accZ, 8); + + // @ 0G + FACTORY_MID( accX, ((pec->calF[0] <<2) | ((pec->calF[3] >>4) &0x3)) ) ; + FACTORY_MID( accY, ((pec->calF[1] <<2) | ((pec->calF[3] >>2) &0x3)) ) ; + FACTORY_MID( accZ, ((pec->calF[2] <<2) | ((pec->calF[3] ) &0x3)) ) ; + + // @ 1G + FACTORY_HI( accX, ((pec->calF[4] <<2) | ((pec->calF[7] >>4) &0x3)) ) ; + FACTORY_HI( accY, ((pec->calF[5] <<2) | ((pec->calF[7] >>2) &0x3)) ) ; + FACTORY_HI( accZ, ((pec->calF[6] <<2) | ((pec->calF[7] ) &0x3)) ) ; + + // Joysticks + FACTORY_LO( joyX, pec->calF[ 9] ) ; + FACTORY_MID(joyX, pec->calF[10] ) ; + FACTORY_HI( joyX, pec->calF[ 8] ) ; + + FACTORY_LO( joyY, pec->calF[12] ) ; + FACTORY_MID(joyY, pec->calF[13] ) ; + FACTORY_HI( joyY, pec->calF[11] ) ; + } + if (c & CAL_TRACK) { // track maximum and minimum values seen + TRACK_LO_HI(accX); + TRACK_LO_HI(accY); + TRACK_LO_HI(accZ); + + TRACK_LO_HI(joyX); + TRACK_LO_HI(joyY); + } + if (c & CAL_RANGE) { // perform software calibration step + RANGE_LO_HI(accX); + RANGE_LO_HI(accY); + RANGE_LO_HI(accZ); + + if (!(c & CAL_NOTJOY)) { // double negative! + RANGE_LO_HI(joyX); + RANGE_LO_HI(joyY); + } + } + if (c & CAL_CENTRE) { // reset centre point of joystick + CENTRE(accX); + CENTRE(accY); + CENTRE(accZ); + + CENTRE(joyX); + CENTRE(joyY); + } +} + +//============================================================================= ======================================== +// Accelerometer screen ...might this be useful for other controllers? +// +// https://bootlin.com/labs/doc/nunchuk.pdf +// X : Move Left/Right : -left / +right +// Y : Move Fwd/Bkwd : -fwd / +bkwd +// Z : Move Down/Up : -down / +up +// +// Movement in the direction of an axis changes that axis reading +// Twisting/tilting around an axis changes the other two readings +// +// EG. Move left will effect X ; turn left will effect Y & Z +// +#define aw 110 // axis width +#define ah 15 // height {0......7......14} +#define am 7 // midpoint { 7 } +#define ar 7 // range {1234567 1234567} + +enum { + ACC_X = 0, + ACC_Y = 1, + ACC_Z = 2, + ACC_CNT = 3, + ACC_1 = ACC_X, // first + ACC_N = ACC_Z, // last +}; + +//+============================================================================ +static +void nunchuck_showAcc (Canvas* const canvas, state_t* const state) +{ + ecDecNunchuck_t* d = &state->ec.dec[state->ec.decN].nunchuck; + ecCalNunchuck_t* lo = &state->ec.calS.nunchuck[1]; + ecCalNunchuck_t* mid = &state->ec.calS.nunchuck[2]; + ecCalNunchuck_t* hi = &state->ec.calS.nunchuck[3]; + + int y[ACC_CNT] = {0, 0+(ah+4), 0+((ah+4)*2)}; + int x = 10; + + static uint16_t v[ACC_CNT][aw] = {0}; +// static uint16_t tv[ACC_CNT][aw] = {0}; + + static uint16_t idx = 0; + static uint16_t cnt = aw -1; + + // Only record when scanner NOT-paused + if (!state->pause) { + uint16_t dead = (1<<5); + + // Find axes y-offsets + for (int a = ACC_1; a <= ACC_N; a++) { + uint16_t* dp = NULL; // data value (current reading) + uint16_t* lp = NULL; // lo value + uint16_t* mp = NULL; // mid value + uint16_t* hp = NULL; // hi value + uint16_t* vp = NULL; // value (result) + + switch (a) { + case ACC_X: + dp = & d->accX; // data (input) + lp = & lo->accX; // low \. + mp = &mid->accX; // mid > calibration + hp = & hi->accX; // high / + vp = &v[ ACC_X][idx]; // value (where to store the result) + break; + case ACC_Y: + dp = & d->accY; + lp = & lo->accY; + mp = &mid->accY; + hp = & hi->accY; + vp = &v[ ACC_Y][idx]; + break; + case ACC_Z: + dp = & d->accZ; + lp = & lo->accZ; + mp = &mid->accZ; + hp = & hi->accZ; + vp = &v[ ACC_Z][idx]; + break; + default: break ; + } + + // Again - qv. the joysick calibration: + // This is not the "right way" to do this, it is just "one way" to do it + // ...mid point and extreme zones have a deadzone + // ...the rest is evenly divided by the amount of space on the graph + if ((*dp >= (*mp -dead)) && (*dp <= (*mp +dead))) *vp = ar ; + else if (*dp >= (*hp -dead)) *vp = ah-1 ; + else if (*dp <= (*lp +dead)) *vp = 0 ; + else if (*dp < *mp) { + uint16_t min = ((*lp +dead) +1); + uint16_t max = ((*mp -dead) -1); + float range = (max -min) +1; + float m = range /(ar-1); // 6 evenly(/fairly) divided zones + *vp = ((int)((*dp -min) /m)) +1; + + } else {//if (*dp > *mp) + uint16_t min = ((*mp +dead) +1); + uint16_t max = ((*hp -dead) -1); + float range = (max -min) +1; + float m = range /(ar-1); // 6 evenly(/fairly) divided zones + *vp = ((int)((*dp -min) /m)) +1 +ar; + } + } + +//! If we decide to offer "export to CSV" +//! I suggest we keep a second array of true-values, rather than do all the maths every time +//! Also - the data will need to me moved to the 'state' table - so a.n.other function can save it off +// tv[ACC_X][idx] = d->accX; +// tv[ACC_Y][idx] = d->accY; +// tv[ACC_Z][idx] = d->accZ; + + // Prepare for the next datapoint + if (++idx >= aw) idx = 0 ; + if (cnt) cnt-- ; + } + + // Auto-pause + if (state->apause && !idx) state->pause = true ; + + // *** Draw axes *** + show(canvas, 0,y[ACC_X] +((ah -img_6x8_X.h) /2), &img_6x8_X, SHOW_SET_BLK); + show(canvas, 0,y[ACC_Y] +((ah -img_6x8_Y.h) /2), &img_6x8_Y, SHOW_SET_BLK); + show(canvas, 0,y[ACC_Z] +((ah -img_6x8_Z.h) /2), &img_6x8_Z, SHOW_SET_BLK); + + canvas_set_color(canvas, ColorBlack); + for (int a = ACC_1; a <= ACC_N; a++) { + canvas_draw_line(canvas, x-1,y[a] , x -1,y[a]+ah); + canvas_draw_line(canvas, x ,y[a]+ah, x+aw-1,y[a]+ah); + + // Mid & Peak lines + for (int i = 1; i < aw; i += 3) { + canvas_draw_dot(canvas, x+i,y[a]); + canvas_draw_dot(canvas, x+i,y[a] +(ah /2)); + } + } + + // Data (wiper display - see notes.txt for scrolling algorithm) + int end = idx ? idx : aw; + for (int a = ACC_1; a <= ACC_N; a++) { + canvas_draw_dot(canvas, x,y[a]+v[a][idx]); + for (int i = 1; i < end; i++) + canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]); + if (!state->apause) + for (int i = end+10; i < aw -cnt; i++) + canvas_draw_line(canvas, x+i,y[a]+v[a][i-1] , x+i,y[a]+v[a][i]); + } + // Wipe bar + if (end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1); + if (++end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1); + if (++end < aw) canvas_draw_line(canvas, x+end,y[0], x+end,y[2]+ah-1); + + // *** Mode buttons *** + show(canvas, 0,55, &img_key_L, SHOW_SET_BLK); // mode key + + if ((state->calib & CAL_RANGE) || state->pause) state->flash++ ; + + // -pause- ...yeah, this got a little out of hand! LOL! + if (state->pause || state->apause) { + if (state->pause && state->apause && !idx) { + if (state->flash &8) { + show(canvas, 108,56, &img_key_U, SHOW_SET_BLK); + } else { + show(canvas, 108,56, &img_key_Ui, SHOW_SET_BLK); + canvas_draw_line(canvas, x+aw,y[0], x+aw,y[2]+ah-1); + } + } else { + show(canvas, 108,56, &img_key_Ui, SHOW_SET_BLK); + } + } else { + show(canvas, 108,56, &img_key_U, SHOW_SET_BLK); // pause + } + + // -calibration- + if (state->calib & CAL_RANGE) { + show(canvas, 119,55, (state->flash &8) ? &img_key_OKi : &img_key_OK, SHOW_SET_BLK); + } else { + show(canvas, 119,55, &img_key_OK, SHOW_SET_BLK); + } +} + +# undef aw +# undef ah +# undef am +# undef ar + +//+============================================================================ ======================================== +// Default nunchuck screen +// +void nunchuck_show (Canvas* const canvas, state_t* const state) +{ + // Nunchucks have TWO scenes + if (state->scene == SCENE_NUNCHUCK_ACC) return nunchuck_showAcc(canvas, state) ; + + // Default scene + ecDecNunchuck_t* d = &state->ec.dec[state->ec.decN].nunchuck; + ecCalNunchuck_t* c = (state->hold) ? &state->ec.calS.nunchuck[(state->hold < 0) ? 0 : 4] + : (ecCalNunchuck_t*)d ; //! danger will robinson! + ecCalNunchuck_t* js = state->ec.calS.nunchuck; + + // X, Y, Z + show(canvas, 42,0, &img_6x8_X, SHOW_SET_BLK); + show(canvas, 73,0, &img_6x8_Y, SHOW_SET_BLK); + show(canvas, 104,0, &img_6x8_Z, SHOW_SET_BLK); + + canvas_draw_str_aligned(canvas, 0,14, AlignLeft, AlignTop, "Accel"); + canvas_draw_str_aligned(canvas, 0,28, AlignLeft, AlignTop, "Joy"); + + // accel values + showHex(canvas, 34,12, c->accX, 3,2); + showHex(canvas, 65,12, c->accY, 3,2); + showHex(canvas, 96,12, c->accZ, 3,2); + // Joy values + showHex(canvas, 38,27, c->joyX, 2,2); + showHex(canvas, 69,27, c->joyY, 2,2); + + showJoy(canvas, 103,32, js[1].joyX, js[2].joyX, js[3].joyX, + js[1].joyY, js[2].joyY, js[3].joyY, d->joyX,d->joyY, 8); + + // buttons + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, 0,44, AlignLeft, AlignTop, "Button"); + + if (!d->btnC) { + canvas_draw_rframe(canvas, 36,42, 18,12, 6); + show(canvas, 42,44, &img_6x8_C, SHOW_SET_BLK); + } else { + canvas_draw_rbox(canvas, 36,42, 18,12, 6); + show(canvas, 42,44, &img_6x8_C, SHOW_SET_WHT); + canvas_set_color(canvas, ColorBlack); + } + + if (!d->btnZ) { + canvas_draw_rframe(canvas, 64,40, 24,16, 2); + show(canvas, 73,44, &img_6x8_Z, SHOW_SET_BLK); + } else { + canvas_draw_rbox(canvas, 64,40, 24,16, 2); + show(canvas, 73,44, &img_6x8_Z, SHOW_SET_WHT); + } + + // Navigation + showPeakHold(state, canvas, state->hold); // peak keys + show(canvas, 0,55, &img_key_L, SHOW_SET_BLK); // mode keys + show(canvas, 9,55, &img_key_R, SHOW_SET_BLK); +} + +//+============================================================================ ======================================== +static +bool nunchuck_keyAcc (const eventMsg_t* const msg, state_t* const state) +{ + int used = false; // assume key is NOT-handled + + switch (msg->input.type) { + case InputTypeShort: //# input.key) { + case InputKeyDown: //# pause) state->pause = false ; // Paused? Restart + else state->apause = !state->apause ; // No? toggle auto-pause + used = true; + break; + + case InputKeyLeft: //# calib &= ~CAL_NOTJOY; // DO calibrate joystick in NUNCHUCK mode + used = true; + break; + + default: break ; //# scene == SCENE_NUNCHUCK_ACC) return nunchuck_keyAcc(msg, state) ; + + // Default scene + int used = false; // assume key is NOT-handled + + switch (msg->input.type) { + case InputTypeShort: //# input.key) { + case InputKeyLeft: //# calib |= CAL_NOTJOY; // do NOT calibrate joystick in _ACC mode + used = true; + break; + default: break ; //# +#include + +//----------------------------------------------------------------------------- +// Controllers which have calibration must have their calibratable controls here +//! Is there a better way to get the start of the decode struct to match the calibration struct ? +#define NUNCHUCK_ANALOGUE \ + uint8_t joyX, joyY; \ + uint16_t accX, accY, accZ; + +//----------------------------------------------------------------------------- +// Calibratable controls +// +typedef + struct ecCalNunchuck { + NUNCHUCK_ANALOGUE + } +ecCalNunchuck_t; + +//----------------------------------------------------------------------------- +// All controls +// +typedef + struct ecDecNunchuck { + NUNCHUCK_ANALOGUE // MUST be first + + // Digital controls + bool btnC, btnZ; // BTN{c, z} + } +ecDecNunchuck_t; + +#undef NUNCHUCK_ANALOGUE + +//============================================================================= +// Function prototypes +// +#include // Canvas +typedef struct wiiEC wiiEC_t ; +typedef enum ecCalib ecCalib_t ; +typedef struct state state_t ; +typedef struct eventMsg eventMsg_t ; + +void nunchuck_decode (wiiEC_t* const pec) ; +void nunchuck_msg (wiiEC_t* const pec, FuriMessageQueue* const queue) ; +void nunchuck_calib (wiiEC_t* const pec, ecCalib_t c) ; + +void nunchuck_show (Canvas* const canvas, state_t* const state) ; +bool nunchuck_key (const eventMsg_t* const msg, state_t* const state) ; + +#endif //WII_EC_NUNCHUCK_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_udraw.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_udraw.c new file mode 100644 index 000000000..babbc92ab --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_udraw.c @@ -0,0 +1,145 @@ +//! udraw support is NOT written - this is just notes about the init function +#include +#include // Core API + +#include "wii_anal.h" +#include "wii_ec.h" +#include "bc_logging.h" + +#include "i2c_workaround.h" //! temporary workaround for a bug in furi i2c [see header] + +// ** If you want to see what this source code looks like with all the MACROs expanded +// ** grep -v '#include ' wii_ec_udraw.c | gcc -E -o /dev/stdout -xc - +# include "wii_ec_macros.h" + +//+============================================================================ ======================================== +// https://github.com/madhephaestus/WiiChuck/blob/master/src/Drawsome.cpp#L3 +// Gratuitously stolen ... never tested (don't own one) - just bought one on ebay +// although it seems like the UK version is a "uDraw" and MIGHT contain a different chipset :/ +// +// read 6 bytes starting from 0x20 +// read 6 bytes starting from 0x28 +// read 6 bytes starting from 0x30 +// read 6 bytes starting from 0x38 +// read 6 bytes starting from 0x00 (#1) +// read 6 bytes starting from 0x00 (#2) +// write 1 byte [0x01] to 0xFB +// read 6 bytes starting from 0x00 (#3) +// read 6 bytes starting from 0x00 (#4) +// +bool udraw_init (wiiEC_t* const pec) +{ + ENTER; + bool rv = true; + +(void)pec; +/* +//! this is the Drawsome code, NOT the uDraw code !! + static const uint8_t reg[9] = {0x20, 0x28, 0x30, 0x38, 0x00, 0x00, 0xFB, 0x00, 0x00}; // 0..8 + const uint8_t* p = reg; + uint8_t buf[6] = {0}; + + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 0 + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 1 + furi_delay_ms(100); + + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 2 + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 3 + furi_delay_ms(100); + + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 4 + furi_delay_ms(100); + + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 5 + furi_delay_ms(100); + + buf[0] = *p++; + buf[1] = 0x01; + if (!furi_hal_i2c_tx(bus,addr, buf,2, timeout)) goto fail ; // 6 + + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 7 + furi_delay_ms(100); + + if (!furi_hal_i2c_trxd(bus,addr, p++,1, buf,sizeof(buf), timeout,300)) goto fail ; // 8 + furi_delay_ms(100); + + TRACE("%s : OK #%d", __func__, (p-reg)); + goto done; + +fail: + ERROR("%s : fail #%d", __func__, (p -reg) -1); + rv = false; + +done: +*/ + LEAVE; + return rv; +} + +//+============================================================================ ======================================== +bool udraw_key (const eventMsg_t* const msg, state_t* const state) +{ +(void)state; + bool run = true; + + switch (msg->input.type) { + case InputTypeShort: //# input.key) { + case InputKeyUp: //# ! After INPUT_LONG_PRESS interval, asynch to InputTypeRelease + switch (msg->input.key) { + case InputKeyUp: //# >U [ LONG-UP ] + case InputKeyDown: //# >D [ LONG-DOWN ] + case InputKeyLeft: //# >L [ LONG-LEFT ] + case InputKeyRight: //# >R [ LONG-RIGHT ] + case InputKeyOk: //# >O [ LONG-OK ] + case InputKeyBack: //# >B [ LONG-BACK ] + default: break ; //# >? + } + break; + case InputTypePress: //# +! After debounce + switch (msg->input.key) { + case InputKeyUp: //# +U [ SHORT-UP ] + case InputKeyDown: //# +D [ SHORT-DOWN ] + case InputKeyLeft: //# +L [ SHORT-LEFT ] + case InputKeyRight: //# +R [ SHORT-RIGHT ] + case InputKeyOk: //# +O [ SHORT-OK ] + case InputKeyBack: //# +B [ SHORT-BACK ] + default: break ; //# +? + } + break; + case InputTypeRepeat: //# *! With INPUT_REPEATE_PRESS period after InputTypeLong event + switch (msg->input.key) { + case InputKeyUp: //# *U [ REPEAT-UP ] + case InputKeyDown: //# *D [ REPEAT-DOWN ] + case InputKeyLeft: //# *L [ REPEAT-LEFT ] + case InputKeyRight: //# *R [ REPEAT-RIGHT ] + case InputKeyOk: //# *O [ REPEAT-OK ] + case InputKeyBack: //# *B [ REPEAT-BACK ] + default: break ; //# *? + } + break; + case InputTypeRelease: //# -! After debounce + switch (msg->input.key) { + case InputKeyUp: //# -U [ RELEASE-UP ] + case InputKeyDown: //# -D [ RELEASE-DOWN ] + case InputKeyLeft: //# -L [ RELEASE-LEFT ] + case InputKeyRight: //# -R [ RELEASE-RIGHT ] + case InputKeyOk: //# -O [ RELEASE-OK ] + case InputKeyBack: //# -B [ RELEASE-BACK ] + default: break ; //# -? + } + break; + default: return true ; + } + + return run; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_udraw.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_udraw.h new file mode 100644 index 000000000..1721894e5 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_ec_udraw.h @@ -0,0 +1,18 @@ +#ifndef WII_EC_UDRAW_H_ +#define WII_EC_UDRAW_H_ + +#include +#include + +//============================================================================= ======================================= +// Function prototypes +// +typedef struct wiiEC wiiEC_t ; +typedef enum ecCalib ecCalib_t ; +typedef struct eventMsg eventMsg_t ; +typedef struct state state_t ; + +bool udraw_init (wiiEC_t* const pec) ; +bool udraw_key (const eventMsg_t* const msg, state_t* const state) ; + +#endif //WII_EC_UDRAW_H_ diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_i2c.c b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_i2c.c new file mode 100644 index 000000000..90fe22163 --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_i2c.c @@ -0,0 +1,309 @@ +//----------------------------------------------------------------------------- ---------------------------------------- +// Biblio: [standing on the shoulders of giants] +// https://bootlin.com/labs/doc/nunchuk.pdf +// https://www.hackster.io/infusion/using-a-wii-nunchuk-with-arduino-597254#toc-i2c-protocol-9 +// https://web.archive.org/web/20220000000000*/https://www.hackster.io/infusion/using-a-wii-nunchuk-with-arduino-597254 +// https://github.com/madhephaestus/WiiChuck/blob/master/src/Accessory.cpp#L14 +// https://wiibrew.org/wiki/Wiimote/Extension_Controllers +// https://www.best-microcontroller-projects.com/i2c-tutorial.html +// +// WiiMote Extension Controller: +// Bus Address : 0x52 +// Register autoincrements after each (byte is) read +// 0x00..0x05 ( 6 bytes) ... [r] Controller Data +// 0x20..0x2F (16 bytes) ... [r] Calibration Data +// 0x30..0x3F (16 bytes) ... [r] (A copy of the) Calibration Data +// 0x40..0x4F (16 bytes) ... [w] Encryption key(s) +// 0xFA..0xFF ( 6 bytes) ... [r] Perhipheral ID + +//----------------------------------------------------------------------------- ---------------------------------------- +#include +#include +#include + +#include +#include +#include + +#include "i2c_workaround.h" //! temporary workaround for a bug in furi i2c [see header] + +#include "wii_anal.h" +#include "wii_i2c.h" +#include "wii_ec.h" + +#include "bc_logging.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +// Wii Extension Controller i2c Bus address +static const uint8_t ec_i2cAddr = 0x52; + +// Initialise for UNencrypted comms +static const uint8_t regInit1 = 0xF0; +static const uint8_t regInit2 = 0xFB; +static const uint8_t cmdInit1[] = {regInit1, 0x55}; +static const uint8_t cmdInit2[] = {regInit2, 0x00}; + +// Initialise for ENcrypted comms +static const uint8_t regInitEnc = 0x40; +static const uint8_t cmdInitEnc[] = {regInitEnc, 0x00}; + +// Crypto key (PSK), base register : {0x40..0x4F}[2][8] +static const uint8_t regEnc = 0x40; // ENC_LEN + +// Controller State data, base register : {0x00..0x05}[6] +static const uint8_t regJoy = 0x00; // JOY_LEN + +// Calibration data, base register : {0x20..0x2F}[16] +static const uint8_t regCal = 0x20; // CAL_LEN + +// Controller ID, base register : {0xFA..0xFF}[6] +static const uint8_t regPid = 0xFA; // PID_LEN + +//+============================================================================ ======================================== +// Hexdump a buffer to the logfile +// +#if LOG_LEVEL >= 4 // INFO + +static +void dump (const uint8_t* buf, const unsigned int len, const char* id) +{ + // snprintf() would be useful! + char s[128] = {0}; + char* p = NULL; + + strcpy(s, id); + p = s +strlen(s); + *p++ = ':'; + *p++ = ' '; + *p++ = '{'; + + for (unsigned int i = 0; i < len; i++) { + uint8_t hi = (buf[i] &0xF0) >>4; + uint8_t lo = (buf[i] &0x0F); + + hi = hi + ((hi > 9) ? ('A' -10) : '0'); + lo = lo + ((lo > 9) ? ('A' -10) : '0'); + + *p++ = (char)hi; + *p++ = (char)lo; + *p++ = ','; + } + *p = '\0'; + *--p = '}'; + INFO(s); +} + +#else +# define dump(...) +#endif + +//+============================================================================ ======================================== +// +//! -W-A-R-N-I-N-G- : THIS ENCRYPTION CODE SHOULD NEVER BE REQUIRED ... AS SUCH, I'VE NEVER TESTED IT +// +static +void decrypt (uint8_t* buf, const uint8_t* encKey, const uint8_t reg, unsigned int len) +{ +#if 1 // Use standard algorithm + // decrypted_byte = (encrypted_byte XOR encKey[1][address%8]) + encKey[2][address%8] + for (uint8_t* p = buf; p < buf+len; p++) + *p = (*p ^ encKey[(reg +(p -buf)) %8]) + encKey[8 +((reg +(p -buf)) %8)]; + +#else //! This is (I think) a shortcut for an all-zero key [not tested] + (void)encKey; + (void)reg; + for (uint8_t* p = buf; p < buf+len; p++) + *p = (*p ^ 0x17) + 0x17; +#endif +} + +//+============================================================================ ======================================== +// Read the Extension Controller state +// ...and decode it in to something sane +// +// Returns: {0:OK, >0:Error} +// +int ecRead (wiiEC_t* pec) +{ + ENTER; + int rv = 0; // assume success + + if (!pec->init) { + WARN("%s : device not initialised", __func__); + rv = 1; + goto bail; + } + + if (!furi_hal_i2c_is_device_ready(i2cBus,i2cAddr, i2cTimeout)) { + INFO("%s : device disconnected", __func__); + pec->init = false; + rv = 2; + goto bail; + } + + if (!furi_hal_i2c_trxd(i2cBus,i2cAddr, ®Joy,1, pec->joy,JOY_LEN, i2cTimeout,i2cReadWait)) { + ERROR("%s : trxd fail", __func__); + rv = 3; + goto bail; + } + + if (pec->encrypt) decrypt(pec->joy, pec->encKey, regJoy, JOY_LEN) ; + + // Decode the readings (according to Controller type) + ecDecode(pec); + +bail: + LEAVE; + return rv; +} + +//+============================================================================ ======================================== +// Initialise an Extension Controller +// +//! To disable encryption, pass a NULL encryption key <-- this is currently ALWAYS the case +// +bool ecInit (wiiEC_t* pec, const uint8_t* encKey) +{ + ENTER; + + bool rv = false; // assume failure + +#if 0 //! i2c workaround + //! I think this is done during OS startup - long before the plugin starts + furi_hal_i2c_init(); +#endif + +#if 0 //! i2c workaround + // May become relevant when the i2c issues are resolved + // Take control of the i2c bus [which returns void !?] + // --> firmware/targets/f7/furi_hal/furi_hal_i2c.c + furi_hal_i2c_acquire(i2cBus); +#endif + + pec->init = false; // assume failure + + // === See if the device is alive === + if (!furi_hal_i2c_is_device_ready(i2cBus,i2cAddr, i2cTimeout)) { + TRACE("%s : waiting for device", __func__); + goto bail; + } + INFO("%s : device connected", __func__); + + // === Initialise the device === + pec->init = false; // This goes true AFTER the (optional) controller-specific init code + + // === Start the Extension Controller === + if (encKey) { //! start in encrypted mode + + //! todo - should this happen here, or AFTER we've got the ID ? + + } else { + + if ( !furi_hal_i2c_tx(i2cBus,i2cAddr, cmdInit1,sizeof(cmdInit1), i2cTimeout) ) { + ERROR("%s : init fail (dec1)", __func__); + goto bail; + } + TRACE("%s : init OK1", __func__); + + if ( !furi_hal_i2c_tx(i2cBus,i2cAddr, cmdInit2,sizeof(cmdInit2), i2cTimeout) ) { + ERROR("%s : init fail (dec2)", __func__); + goto bail; + } + TRACE("%s : init OK2", __func__); + } + + // === Retrieve the Extension Controller ID === + if (!furi_hal_i2c_trx(i2cBus,i2cAddr, ®Pid,1, pec->pid,PID_LEN, i2cTimeout)) { + ERROR("%s : T(R)x fail (pid)", __func__); + goto bail; + } + if (pec->encrypt) decrypt(pec->joy, pec->encKey, regJoy, JOY_LEN); + dump(pec->pid, PID_LEN, "pid"); // debug INFO + + // Find the StringID in the lookup table + for (pec->pidx = PID_FIRST; pec->pidx < PID_ERROR; pec->pidx++) + if (memcmp(pec->pid, ecId[pec->pidx].id, PID_LEN) == 0) break ; + if (pec->pidx == PID_ERROR) pec->pidx = PID_UNKNOWN ; + pec->sid = ecId[pec->pidx].name; + INFO("sid: %s", pec->sid); + + // === (optionally) Enable encryption === + if (!encKey) { + pec->encrypt = false; + + } else { // Controller WILL encrypt ALL tranmissions +//! this encryption code fails - should it be done earlier? +//! as it is probably never of any use, I'm kinda loathed to spend time on it +//! https://github.com/madhephaestus/WiiChuck/blob/master/src/Accessory.cpp#L138 + uint8_t encTx[1+ENC_LEN] = {0}; + uint8_t* ep = encTx; + + pec->encrypt = true; + + // ** Start the Controller in ENcrytped mode + if ( !furi_hal_i2c_tx(i2cBus,i2cAddr, cmdInitEnc,sizeof(cmdInitEnc), i2cTimeout) ) { + ERROR("%s : init fail (enc)", __func__); + goto bail; + } + + // Copy the (symmetric) encryption key to the controller state table + if (pec->encKey != encKey) + memcpy(pec->encKey, encKey, ENC_LEN); + + // Build the encryption key packet + *ep++ = regEnc; + memcpy(ep, pec->encKey, ENC_LEN); + + // ** Send encryption key (PSK) + if ( !furi_hal_i2c_tx(i2cBus,i2cAddr, encTx,(1+ENC_LEN), i2cTimeout) ) { + ERROR("%s : key fail", __func__); + goto bail; + } + + TRACE("%s : init OK (enc)", __func__); + } + + // === Some devices [eg. Drawsome/uDraw] require additional init code === + if ( ecId[pec->init].init && (ecId[pec->init].init(pec) == false) ) goto bail ; + pec->init = true; + + // === Read calibration data === + if (!furi_hal_i2c_trx(i2cBus,i2cAddr, ®Cal,1, pec->calF,CAL_LEN, i2cTimeout)) { + ERROR("%s : trx fail (cal)", __func__); + goto bail; + } + if (pec->encrypt) decrypt(pec->joy, pec->encKey, regJoy, JOY_LEN); + dump(pec->calF, CAL_LEN, "cal"); + + ecCalibrate(pec, CAL_RESET | CAL_FACTORY); // Load factory default calibration + + // === Initialise decode buffers === + pec->decN = 0; // read in to decode[1] (yes, N=0 -> read in to dec[1]) + switch (ecRead(pec)) { + case 0: // read OK + memcpy(&pec->dec[0], &pec->dec[1], sizeof(pec->dec[0])); + dump(pec->joy, JOY_LEN, "joy"); + break; + + default: // bug: unknown + case 1: // bug: not initialised - should never happen + ERROR("%s : read bug", __func__); + break; + + case 2: // device gone + case 3: // read fail + // Logging done by ecRead() + pec->init = false; + goto bail; + } + + rv = true; // yay :) + +bail: +#if 0 //! i2c workaround + furi_hal_i2c_release(i2cBus); +#endif + + LEAVE; + return rv; +} diff --git a/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_i2c.h b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_i2c.h new file mode 100644 index 000000000..18cb5ee9b --- /dev/null +++ b/Applications/Official/DEV_FW/source/xMasterX/wii_ec_anal/wii_i2c.h @@ -0,0 +1,42 @@ +#ifndef WII_I2C_H_ +#define WII_I2C_H_ + +#include + +//#include "wii_ec.h" + +//----------------------------------------------------------------------------- ---------------------------------------- +// i2c bus details +// +// https://www.best-microcontroller-projects.com/i2c-tutorial.html +// https://web.archive.org/web/20220000000000*/https://www.best-microcontroller-projects.com/i2c-tutorial.html +// https://training.ti.com/introduction-i2c-reserved-addresses +// +// After the (special) START "bit"... +// the first 8bits (byte) of i2c data are the 7bit i2c Address, +// FOLLOWED by 1bit to signify a READ or WRITE {0=write, 1=read} +// The data is transmitted BIG-Endian, IE. MSb first [human readable] +// So the address actually lives in the TOP (MSb's) of the first "byte", (with bit0 being used as the read/write flag) +// +// The read() and write() functions on the FZ will set the LSb appropriately, +// BUT they do NOT shift the address left to make room for it! +// So the address you give to read/write() MUST be given as (7bitAddress << 1) +// +// When we read: After we send the read command, we wait for i2cReadWait uS before reading the data +// + +// firmware/targets/f7/furi_hal/furi_hal_i2c_types.h +#define i2cBus (&furi_hal_i2c_handle_external) // FZ external i2c bus +#define i2cAddr (ec_i2cAddr << 1) +#define i2cTimeout (3) // in mS +#define i2cReadWait (300) //! 300uS: how low can we take this? + +//----------------------------------------------------------------------------- ---------------------------------------- +// public functions +// +typedef struct wiiEC wiiEC_t ; + +bool ecInit (wiiEC_t* const pec, const uint8_t* encKey) ; +int ecRead (wiiEC_t* const pec) ; + +#endif //WII_I2C_H_