From df808be8d741b8920d09a92671ddce7712b4f519 Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Thu, 8 Dec 2022 12:31:22 +0300 Subject: [PATCH 01/21] [FL-3003] Fix logical error in storage script (#2105) * Fix logical error in storage script * Fix formatting --- scripts/flipper/storage.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 6b53f7477..9c9f52958 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -238,9 +238,9 @@ class FlipperStorage: while read_size < size: self.read.until("Ready?" + self.CLI_EOL) self.send("y") - read_size = min(size - read_size, buffer_size) - filedata.extend(self.port.read(read_size)) - read_size = read_size + read_size + chunk_size = min(size - read_size, buffer_size) + filedata.extend(self.port.read(chunk_size)) + read_size = read_size + chunk_size percent = str(math.ceil(read_size / size * 100)) total_chunks = str(math.ceil(size / buffer_size)) From b85f533a20c8eb5a6ababf20710263002a9096bd Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Fri, 9 Dec 2022 21:03:19 +0300 Subject: [PATCH 02/21] VCP session close fix (#2108) * VCP thread check before flag set * VCP running flag check * Cli vcp: more clear CLI_VCP_DEBUG logs * Cli: move tag to debug log macro Co-authored-by: SG --- applications/services/cli/cli_vcp.c | 71 +++++++++++++---------------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/applications/services/cli/cli_vcp.c b/applications/services/cli/cli_vcp.c index ad26dca47..454c99d7a 100644 --- a/applications/services/cli/cli_vcp.c +++ b/applications/services/cli/cli_vcp.c @@ -11,6 +11,12 @@ #define VCP_IF_NUM 0 +#ifdef CLI_VCP_DEBUG +#define VCP_DEBUG(...) FURI_LOG_D(TAG, __VA_ARGS__) +#else +#define VCP_DEBUG(...) +#endif + typedef enum { VcpEvtStop = (1 << 0), VcpEvtConnect = (1 << 1), @@ -104,9 +110,8 @@ static int32_t vcp_worker(void* context) { // VCP session opened if(flags & VcpEvtConnect) { -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "Connect"); -#endif + VCP_DEBUG("Connect"); + if(vcp->connected == false) { vcp->connected = true; furi_stream_buffer_send(vcp->rx_stream, &ascii_soh, 1, FuriWaitForever); @@ -115,9 +120,8 @@ static int32_t vcp_worker(void* context) { // VCP session closed if(flags & VcpEvtDisconnect) { -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "Disconnect"); -#endif + VCP_DEBUG("Disconnect"); + if(vcp->connected == true) { vcp->connected = false; furi_stream_buffer_receive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); @@ -127,9 +131,8 @@ static int32_t vcp_worker(void* context) { // Rx buffer was read, maybe there is enough space for new data? if((flags & VcpEvtStreamRx) && (missed_rx > 0)) { -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "StreamRx"); -#endif + VCP_DEBUG("StreamRx"); + if(furi_stream_buffer_spaces_available(vcp->rx_stream) >= USB_CDC_PKT_LEN) { flags |= VcpEvtRx; missed_rx--; @@ -140,9 +143,8 @@ static int32_t vcp_worker(void* context) { if(flags & VcpEvtRx) { if(furi_stream_buffer_spaces_available(vcp->rx_stream) >= USB_CDC_PKT_LEN) { int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "Rx %d", len); -#endif + VCP_DEBUG("Rx %ld", len); + if(len > 0) { furi_check( furi_stream_buffer_send( @@ -150,18 +152,15 @@ static int32_t vcp_worker(void* context) { (size_t)len); } } else { -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "Rx missed"); -#endif + VCP_DEBUG("Rx missed"); missed_rx++; } } // New data in Tx buffer if(flags & VcpEvtStreamTx) { -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "StreamTx"); -#endif + VCP_DEBUG("StreamTx"); + if(tx_idle) { flags |= VcpEvtTx; } @@ -171,9 +170,9 @@ static int32_t vcp_worker(void* context) { if(flags & VcpEvtTx) { size_t len = furi_stream_buffer_receive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "Tx %d", len); -#endif + + VCP_DEBUG("Tx %d", len); + if(len > 0) { // Some data left in Tx buffer. Sending it now tx_idle = false; furi_hal_cdc_send(VCP_IF_NUM, vcp->data_buffer, len); @@ -216,9 +215,7 @@ static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) { return 0; } -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "rx %u start", size); -#endif + VCP_DEBUG("rx %u start", size); size_t rx_cnt = 0; @@ -227,19 +224,21 @@ static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) { if(batch_size > VCP_RX_BUF_SIZE) batch_size = VCP_RX_BUF_SIZE; size_t len = furi_stream_buffer_receive(vcp->rx_stream, buffer, batch_size, timeout); -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "rx %u ", batch_size); -#endif + VCP_DEBUG("rx %u ", batch_size); + if(len == 0) break; + if(vcp->running == false) { + // EOT command is received after VCP session close + rx_cnt += len; + break; + } furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStreamRx); size -= len; buffer += len; rx_cnt += len; } -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "rx %u end", size); -#endif + VCP_DEBUG("rx %u end", size); return rx_cnt; } @@ -251,9 +250,7 @@ static void cli_vcp_tx(const uint8_t* buffer, size_t size) { return; } -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "tx %u start", size); -#endif + VCP_DEBUG("tx %u start", size); while(size > 0 && vcp->connected) { size_t batch_size = size; @@ -261,17 +258,13 @@ static void cli_vcp_tx(const uint8_t* buffer, size_t size) { furi_stream_buffer_send(vcp->tx_stream, buffer, batch_size, FuriWaitForever); furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStreamTx); -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "tx %u", batch_size); -#endif + VCP_DEBUG("tx %u", batch_size); size -= batch_size; buffer += batch_size; } -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "tx %u end", size); -#endif + VCP_DEBUG("tx %u end", size); } static void cli_vcp_tx_stdout(const char* data, size_t size) { From 7fb1af07b8014b843cb6abda9ce2be93d14bda99 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Sat, 10 Dec 2022 15:02:34 +0200 Subject: [PATCH 03/21] [FL- 3014] Untangle NFC from Unit Tests (#2106) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Untangle NFC from Unit Tests * nfc tests: add log error Co-authored-by: Sergey Gavrilov Co-authored-by: gornekich Co-authored-by: あく --- applications/debug/unit_tests/nfc/nfc_test.c | 7 +++++-- applications/main/nfc/nfc_i.h | 4 +--- .../main/nfc/scenes/nfc_scene_generate_info.c | 9 +++++++-- .../main/nfc/scenes/nfc_scene_set_type.c | 2 +- fbt_options.py | 1 - .../main => lib}/nfc/helpers/nfc_generators.c | 17 ----------------- .../main => lib}/nfc/helpers/nfc_generators.h | 7 +++---- 7 files changed, 17 insertions(+), 30 deletions(-) rename {applications/main => lib}/nfc/helpers/nfc_generators.c (95%) rename {applications/main => lib}/nfc/helpers/nfc_generators.h (79%) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index 4218482c7..e9e7b35f6 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -102,7 +102,10 @@ static bool nfc_test_digital_signal_test_encode( do { // Read test data - if(!nfc_test_read_signal_from_file(file_name)) break; + if(!nfc_test_read_signal_from_file(file_name)) { + FURI_LOG_E(TAG, "Failed to read signal from file"); + break; + } // Encode signal FURI_CRITICAL_ENTER(); diff --git a/applications/main/nfc/nfc_i.h b/applications/main/nfc/nfc_i.h index 57eefbf67..d49b84766 100644 --- a/applications/main/nfc/nfc_i.h +++ b/applications/main/nfc/nfc_i.h @@ -27,6 +27,7 @@ #include #include #include +#include #include "views/dict_attack.h" #include "views/detect_reader.h" @@ -50,9 +51,6 @@ typedef enum { NfcRpcStateEmulated, } NfcRpcState; -// Forward declaration due to circular dependency -typedef struct NfcGenerator NfcGenerator; - struct Nfc { NfcWorker* worker; ViewDispatcher* view_dispatcher; diff --git a/applications/main/nfc/scenes/nfc_scene_generate_info.c b/applications/main/nfc/scenes/nfc_scene_generate_info.c index 66900767e..7b84ae43b 100644 --- a/applications/main/nfc/scenes/nfc_scene_generate_info.c +++ b/applications/main/nfc/scenes/nfc_scene_generate_info.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "../helpers/nfc_generators.h" +#include "lib/nfc/helpers/nfc_generators.h" void nfc_scene_generate_info_dialog_callback(DialogExResult result, void* context) { Nfc* nfc = context; @@ -39,7 +39,12 @@ bool nfc_scene_generate_info_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultRight) { - scene_manager_next_scene(nfc->scene_manager, nfc->generator->next_scene); + // Switch either to NfcSceneMfClassicMenu or NfcSceneMfUltralightMenu + if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareClassic) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicMenu); + } else if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightMenu); + } consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index 3e08aeb3f..cadf2eb69 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "../helpers/nfc_generators.h" +#include "lib/nfc/helpers/nfc_generators.h" enum SubmenuIndex { SubmenuIndexNFCA4, diff --git a/fbt_options.py b/fbt_options.py index 7a805c996..4850389ad 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -81,7 +81,6 @@ FIRMWARE_APPS = { "basic_services", "updater_app", "unit_tests", - "nfc", ], } diff --git a/applications/main/nfc/helpers/nfc_generators.c b/lib/nfc/helpers/nfc_generators.c similarity index 95% rename from applications/main/nfc/helpers/nfc_generators.c rename to lib/nfc/helpers/nfc_generators.c index 5f0527c6a..769b9c7b6 100644 --- a/applications/main/nfc/helpers/nfc_generators.c +++ b/lib/nfc/helpers/nfc_generators.c @@ -376,103 +376,86 @@ static void nfc_generate_mf_classic_4k_7b_uid(NfcDeviceData* data) { static const NfcGenerator mf_ul_generator = { .name = "Mifare Ultralight", .generator_func = nfc_generate_mf_ul_orig, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator mf_ul_11_generator = { .name = "Mifare Ultralight EV1 11", .generator_func = nfc_generate_mf_ul_11, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator mf_ul_h11_generator = { .name = "Mifare Ultralight EV1 H11", .generator_func = nfc_generate_mf_ul_h11, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator mf_ul_21_generator = { .name = "Mifare Ultralight EV1 21", .generator_func = nfc_generate_mf_ul_21, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator mf_ul_h21_generator = { .name = "Mifare Ultralight EV1 H21", .generator_func = nfc_generate_mf_ul_h21, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag203_generator = { .name = "NTAG203", .generator_func = nfc_generate_mf_ul_ntag203, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag213_generator = { .name = "NTAG213", .generator_func = nfc_generate_ntag213, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag215_generator = { .name = "NTAG215", .generator_func = nfc_generate_ntag215, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag216_generator = { .name = "NTAG216", .generator_func = nfc_generate_ntag216, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag_i2c_1k_generator = { .name = "NTAG I2C 1k", .generator_func = nfc_generate_ntag_i2c_1k, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag_i2c_2k_generator = { .name = "NTAG I2C 2k", .generator_func = nfc_generate_ntag_i2c_2k, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag_i2c_plus_1k_generator = { .name = "NTAG I2C Plus 1k", .generator_func = nfc_generate_ntag_i2c_plus_1k, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator ntag_i2c_plus_2k_generator = { .name = "NTAG I2C Plus 2k", .generator_func = nfc_generate_ntag_i2c_plus_2k, - .next_scene = NfcSceneMfUltralightMenu, }; static const NfcGenerator mifare_classic_1k_4b_uid_generator = { .name = "Mifare Classic 1k 4byte UID", .generator_func = nfc_generate_mf_classic_1k_4b_uid, - .next_scene = NfcSceneMfClassicMenu, }; static const NfcGenerator mifare_classic_1k_7b_uid_generator = { .name = "Mifare Classic 1k 7byte UID", .generator_func = nfc_generate_mf_classic_1k_7b_uid, - .next_scene = NfcSceneMfClassicMenu, }; static const NfcGenerator mifare_classic_4k_4b_uid_generator = { .name = "Mifare Classic 4k 4byte UID", .generator_func = nfc_generate_mf_classic_4k_4b_uid, - .next_scene = NfcSceneMfClassicMenu, }; static const NfcGenerator mifare_classic_4k_7b_uid_generator = { .name = "Mifare Classic 4k 7byte UID", .generator_func = nfc_generate_mf_classic_4k_7b_uid, - .next_scene = NfcSceneMfClassicMenu, }; const NfcGenerator* const nfc_generators[] = { diff --git a/applications/main/nfc/helpers/nfc_generators.h b/lib/nfc/helpers/nfc_generators.h similarity index 79% rename from applications/main/nfc/helpers/nfc_generators.h rename to lib/nfc/helpers/nfc_generators.h index 362a19b1e..8cee67067 100644 --- a/applications/main/nfc/helpers/nfc_generators.h +++ b/lib/nfc/helpers/nfc_generators.h @@ -1,14 +1,13 @@ #pragma once -#include "../nfc_i.h" +#include "../nfc_device.h" typedef void (*NfcGeneratorFunc)(NfcDeviceData* data); -struct NfcGenerator { +typedef struct { const char* name; NfcGeneratorFunc generator_func; - NfcScene next_scene; -}; +} NfcGenerator; extern const NfcGenerator* const nfc_generators[]; From 9d728a1c65935d9473389fa98c7f1946935e4976 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Sat, 10 Dec 2022 16:10:51 +0300 Subject: [PATCH 04/21] Check FL ticket in PR name after merge (#2076) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add base * Add base again * Test reporting * Fix reporting * Fix secrets * Fix arguments in report * Remove depricated actions * Disable reporting on PR Co-authored-by: あく --- .github/workflows/amap_analyse.yml | 2 +- .github/workflows/build.yml | 13 ++----- .github/workflows/check_submodules.yml | 6 +-- .github/workflows/lint_c.yml | 2 +- .github/workflows/lint_python.yml | 2 +- .github/workflows/merge_report.yml | 41 ++++++++++++++++++++ .github/workflows/pvs_studio.yml | 12 +----- .github/workflows/unit_tests.yml | 2 +- scripts/get_env.py | 38 +++++++++++------- scripts/merge_report_qa.py | 53 ++++++++++++++++++++++++++ 10 files changed, 130 insertions(+), 41 deletions(-) create mode 100644 .github/workflows/merge_report.yml create mode 100755 scripts/merge_report_qa.py diff --git a/.github/workflows/amap_analyse.yml b/.github/workflows/amap_analyse.yml index cfb1eab14..6231c5886 100644 --- a/.github/workflows/amap_analyse.yml +++ b/.github/workflows/amap_analyse.yml @@ -39,7 +39,7 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ed5ee6bd7..99c272e60 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} @@ -35,6 +35,7 @@ jobs: mkdir artifacts - name: 'Get commit details' + id: names run: | if [[ ${{ github.event_name }} == 'pull_request' ]]; then TYPE="pull" @@ -45,14 +46,6 @@ jobs: fi python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - - name: 'Generate suffixes for comment' - id: names - run: | - echo "::set-output name=branch_name::${BRANCH_NAME}" - echo "::set-output name=commit_sha::${COMMIT_SHA}" - echo "::set-output name=default_target::${DEFAULT_TARGET}" - echo "::set-output name=suffix::${SUFFIX}" - - name: 'Bundle scripts' if: ${{ !github.event.pull_request.head.repo.fork }} run: | @@ -143,7 +136,7 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 submodules: true diff --git a/.github/workflows/check_submodules.yml b/.github/workflows/check_submodules.yml index eba4affc3..2eb2027c9 100644 --- a/.github/workflows/check_submodules.yml +++ b/.github/workflows/check_submodules.yml @@ -20,7 +20,7 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} @@ -36,12 +36,12 @@ jobs: BRANCHES=$(git branch -r --contains "$SUBMODULE_HASH"); COMMITS_IN_BRANCH="$(git rev-list --count dev)"; if [ $COMMITS_IN_BRANCH -lt $SUB_COMMITS_MIN ]; then - echo "::set-output name=fails::error"; + echo "name=fails::error" >> $GITHUB_OUTPUT echo "::error::Error: Too low commits in $SUB_BRANCH of submodule $SUB_PATH: $COMMITS_IN_BRANCH(expected $SUB_COMMITS_MIN+)"; exit 1; fi if ! grep -q "/$SUB_BRANCH" <<< "$BRANCHES"; then - echo "::set-output name=fails::error"; + echo "name=fails::error" >> $GITHUB_OUTPUT echo "::error::Error: Submodule $SUB_PATH is not on branch $SUB_BRANCH"; exit 1; fi diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml index 23dc6c699..71ec24ff6 100644 --- a/.github/workflows/lint_c.yml +++ b/.github/workflows/lint_c.yml @@ -23,7 +23,7 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml index c2f092110..44f233db8 100644 --- a/.github/workflows/lint_python.yml +++ b/.github/workflows/lint_python.yml @@ -20,7 +20,7 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/merge_report.yml b/.github/workflows/merge_report.yml new file mode 100644 index 000000000..b6c089056 --- /dev/null +++ b/.github/workflows/merge_report.yml @@ -0,0 +1,41 @@ +name: 'Check FL ticket in PR name' + +on: + push: + branches: + - dev +jobs: + merge_report: + runs-on: [self-hosted,FlipperZeroShell] + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get commit details' + run: | + if [[ ${{ github.event_name }} == 'pull_request' ]]; then + TYPE="pull" + elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + TYPE="tag" + else + TYPE="other" + fi + python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" + + - name: 'Check ticket and report' + run: | + FBT_TOOLCHAIN_PATH=/runner/_work source scripts/toolchain/fbtenv.sh + python3 -m pip install slack_sdk + python3 scripts/merge_report_qa.py \ + ${{ secrets.QA_REPORT_SLACK_TOKEN }} \ + ${{ secrets.QA_REPORT_SLACK_CHANNEL }} + diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index 9de493a44..5473f19f0 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -25,12 +25,13 @@ jobs: fi - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - name: 'Get commit details' + id: names run: | if [[ ${{ github.event_name }} == 'pull_request' ]]; then TYPE="pull" @@ -41,15 +42,6 @@ jobs: fi python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - - name: 'Generate suffixes for comment' - if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }} - id: names - run: | - echo "::set-output name=branch_name::${BRANCH_NAME}" - echo "::set-output name=commit_sha::${COMMIT_SHA}" - echo "::set-output name=default_target::${DEFAULT_TARGET}" - echo "::set-output name=suffix::${SUFFIX}" - - name: 'Make reports directory' run: | rm -rf reports/ diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 1ca4a9c03..4eab21c87 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -12,7 +12,7 @@ jobs: runs-on: [self-hosted, FlipperZeroTest] steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/scripts/get_env.py b/scripts/get_env.py index d05273094..e2da6eda5 100644 --- a/scripts/get_env.py +++ b/scripts/get_env.py @@ -77,28 +77,38 @@ def add_env(name, value, file): print(f"{delimeter}", file=file) -def add_envs(data, env_file, args): - add_env("COMMIT_MSG", data["commit_comment"], env_file) - add_env("COMMIT_HASH", data["commit_hash"], env_file) - add_env("COMMIT_SHA", data["commit_sha"], env_file) - add_env("SUFFIX", data["suffix"], env_file) - add_env("BRANCH_NAME", data["branch_name"], env_file) - add_env("DIST_SUFFIX", data["suffix"], env_file) - add_env("WORKFLOW_BRANCH_OR_TAG", data["branch_name"], env_file) +def add_set_output_var(name, value, file): + print(f"{name}={value}", file=file) + + +def add_envs(data, gh_env_file, gh_out_file, args): + add_env("COMMIT_MSG", data["commit_comment"], gh_env_file) + add_env("COMMIT_HASH", data["commit_hash"], gh_env_file) + add_env("COMMIT_SHA", data["commit_sha"], gh_env_file) + add_env("SUFFIX", data["suffix"], gh_env_file) + add_env("BRANCH_NAME", data["branch_name"], gh_env_file) + add_env("DIST_SUFFIX", data["suffix"], gh_env_file) + add_env("WORKFLOW_BRANCH_OR_TAG", data["branch_name"], gh_env_file) + add_set_output_var("branch_name", data["branch_name"], gh_out_file) + add_set_output_var("commit_sha", data["commit_sha"], gh_out_file) + add_set_output_var("default_target", os.getenv("DEFAULT_TARGET"), gh_out_file) + add_set_output_var("suffix", data["suffix"], gh_out_file) if args.type == "pull": - add_env("PULL_ID", data["pull_id"], env_file) - add_env("PULL_NAME", data["pull_name"], env_file) + add_env("PULL_ID", data["pull_id"], gh_env_file) + add_env("PULL_NAME", data["pull_name"], gh_env_file) def main(): args = parse_args() - event_file = open(args.event_file) + event_file = open(args.event_file, "r") event = json.load(event_file) - env_file = open(os.environ["GITHUB_ENV"], "a") + gh_env_file = open(os.environ["GITHUB_ENV"], "a") + gh_out_file = open(os.environ["GITHUB_OUTPUT"], "a") data = get_details(event, args) - add_envs(data, env_file, args) + add_envs(data, gh_env_file, gh_out_file, args) event_file.close() - env_file.close() + gh_env_file.close() + gh_out_file.close() if __name__ == "__main__": diff --git a/scripts/merge_report_qa.py b/scripts/merge_report_qa.py new file mode 100755 index 000000000..c0707848e --- /dev/null +++ b/scripts/merge_report_qa.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +import os +import re +import sys +import argparse +from slack_sdk import WebClient +from slack_sdk.errors import SlackApiError + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("slack_token") + parser.add_argument("slack_channel") + args = parser.parse_args() + return args + + +def checkCommitMessage(msg): + regex = re.compile(r"^'?\[FL-\d+\]") + if regex.match(msg): + return True + return False + + +def reportSlack(commit_hash, slack_token, slack_channel, message): + client = WebClient(token=slack_token) + try: + client.chat_postMessage(channel="#" + slack_channel, text=message) + except SlackApiError as e: + print(e) + sys.exit(1) + + +def main(): + args = parse_args() + commit_msg = os.getenv("COMMIT_MSG") + commit_hash = os.getenv("COMMIT_HASH") + commit_sha = os.getenv("COMMIT_SHA") + commit_link = ( + "" + ) + message = "Commit " + commit_link + " merged to dev without 'FL' ticket!" + if not checkCommitMessage(commit_msg): + reportSlack(commit_hash, args.slack_token, args.slack_channel, message) + + +if __name__ == "__main__": + main() From 01e24f683793f6ea88ab344c1b8b5fcab90429cb Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 10 Dec 2022 18:30:03 +0300 Subject: [PATCH 05/21] WS: Show received signal age (#2087) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: Show received signal age: by @LY2NEO with some fixes from me * WS: Signal age display GUI fixes and improvements * WeatherStation: refactor variable names * WS: GUI fixes and improvements: add icons by @Karator and apply UI changes * Weather Station: proper event flow for view redraw. Co-authored-by: あく --- .../weather_station/images/Humid_8x13.png | Bin 0 -> 3618 bytes .../weather_station/images/Timer_11x11.png | Bin 0 -> 3616 bytes .../weather_station/protocols/ws_generic.c | 17 ++++ .../weather_station/protocols/ws_generic.h | 1 + .../views/weather_station_receiver.c | 4 +- .../views/weather_station_receiver_info.c | 94 ++++++++++++++---- 6 files changed, 97 insertions(+), 19 deletions(-) create mode 100644 applications/plugins/weather_station/images/Humid_8x13.png create mode 100644 applications/plugins/weather_station/images/Timer_11x11.png diff --git a/applications/plugins/weather_station/images/Humid_8x13.png b/applications/plugins/weather_station/images/Humid_8x13.png new file mode 100644 index 0000000000000000000000000000000000000000..6d8c71b000699e148e9480f7ddf9795b33e2fe7f GIT binary patch literal 3618 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 literal 0 HcmV?d00001 diff --git a/applications/plugins/weather_station/images/Timer_11x11.png b/applications/plugins/weather_station/images/Timer_11x11.png new file mode 100644 index 0000000000000000000000000000000000000000..21ad47f4b2bf69d81929187ca63d7a3d1b111ea4 GIT binary patch literal 3616 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 Y4M;=-T9{t95channel; if(!flipper_format_write_uint32(flipper_format, "Ch", &temp_data, 1)) { FURI_LOG_E(TAG, "Unable to add Channel"); @@ -168,6 +179,12 @@ bool ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipp } instance->humidity = (uint8_t)temp_data; + if(!flipper_format_read_uint32(flipper_format, "Ts", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing timestamp"); + break; + } + instance->timestamp = (uint32_t)temp_data; + if(!flipper_format_read_uint32(flipper_format, "Ch", (uint32_t*)&temp_data, 1)) { FURI_LOG_E(TAG, "Missing Channel"); break; diff --git a/applications/plugins/weather_station/protocols/ws_generic.h b/applications/plugins/weather_station/protocols/ws_generic.h index b2a84df8e..657f8a1fc 100644 --- a/applications/plugins/weather_station/protocols/ws_generic.h +++ b/applications/plugins/weather_station/protocols/ws_generic.h @@ -29,6 +29,7 @@ struct WSBlockGeneric { uint8_t data_count_bit; uint8_t battery_low; uint8_t humidity; + uint32_t timestamp; uint8_t channel; uint8_t btn; float temp; diff --git a/applications/plugins/weather_station/views/weather_station_receiver.c b/applications/plugins/weather_station/views/weather_station_receiver.c index 61b152602..124065e32 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver.c +++ b/applications/plugins/weather_station/views/weather_station_receiver.c @@ -8,7 +8,7 @@ #include #define FRAME_HEIGHT 12 -#define MAX_LEN_PX 100 +#define MAX_LEN_PX 112 #define MENU_ITEMS 4u #define UNLOCK_CNT 3 @@ -189,7 +189,7 @@ void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { canvas_set_color(canvas, ColorBlack); } canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); - canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); + canvas_draw_str(canvas, 14, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); furi_string_reset(str_buff); } if(scrollbar) { diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.c b/applications/plugins/weather_station/views/weather_station_receiver_info.c index 49b447f10..c2fa00641 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver_info.c +++ b/applications/plugins/weather_station/views/weather_station_receiver_info.c @@ -9,9 +9,11 @@ struct WSReceiverInfo { View* view; + FuriTimer* timer; }; typedef struct { + uint32_t curr_ts; FuriString* protocol_name; WSBlockGeneric* generic; } WSReceiverInfoModel; @@ -28,6 +30,10 @@ void ws_view_receiver_info_update(WSReceiverInfo* ws_receiver_info, FlipperForma flipper_format_read_string(fff, "Protocol", model->protocol_name); ws_block_generic_deserialize(model->generic, fff); + + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + model->curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); }, true); } @@ -44,46 +50,76 @@ void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) { "%s %db", furi_string_get_cstr(model->protocol_name), model->generic->data_count_bit); - canvas_draw_str(canvas, 5, 8, buffer); + canvas_draw_str(canvas, 0, 8, buffer); if(model->generic->channel != WS_NO_CHANNEL) { snprintf(buffer, sizeof(buffer), "Ch: %01d", model->generic->channel); - canvas_draw_str(canvas, 105, 8, buffer); + canvas_draw_str(canvas, 106, 8, buffer); } if(model->generic->id != WS_NO_ID) { snprintf(buffer, sizeof(buffer), "Sn: 0x%02lX", model->generic->id); - canvas_draw_str(canvas, 5, 20, buffer); + canvas_draw_str(canvas, 0, 20, buffer); } if(model->generic->btn != WS_NO_BTN) { snprintf(buffer, sizeof(buffer), "Btn: %01d", model->generic->btn); - canvas_draw_str(canvas, 62, 20, buffer); + canvas_draw_str(canvas, 57, 20, buffer); } if(model->generic->battery_low != WS_NO_BATT) { snprintf( buffer, sizeof(buffer), "Batt: %s", (!model->generic->battery_low ? "ok" : "low")); - canvas_draw_str(canvas, 90, 20, buffer); + canvas_draw_str_aligned(canvas, 126, 17, AlignRight, AlignCenter, buffer); } snprintf(buffer, sizeof(buffer), "Data: 0x%llX", model->generic->data); - canvas_draw_str(canvas, 5, 32, buffer); + canvas_draw_str(canvas, 0, 32, buffer); - elements_bold_rounded_frame(canvas, 2, 37, 123, 25); + elements_bold_rounded_frame(canvas, 0, 38, 127, 25); canvas_set_font(canvas, FontPrimary); if(model->generic->temp != WS_NO_TEMPERATURE) { - canvas_draw_icon(canvas, 18, 42, &I_Therm_7x16); + canvas_draw_icon(canvas, 6, 43, &I_Therm_7x16); snprintf(buffer, sizeof(buffer), "%3.1f C", (double)model->generic->temp); - canvas_draw_str_aligned(canvas, 63, 46, AlignRight, AlignTop, buffer); - canvas_draw_circle(canvas, 55, 45, 1); + canvas_draw_str_aligned(canvas, 47, 47, AlignRight, AlignTop, buffer); + canvas_draw_circle(canvas, 38, 46, 1); } if(model->generic->humidity != WS_NO_HUMIDITY) { - canvas_draw_icon(canvas, 75, 42, &I_Humid_10x15); + canvas_draw_icon(canvas, 53, 44, &I_Humid_8x13); snprintf(buffer, sizeof(buffer), "%d%%", model->generic->humidity); - canvas_draw_str(canvas, 91, 54, buffer); + canvas_draw_str(canvas, 64, 55, buffer); + } + + if((int)model->generic->timestamp > 0 && model->curr_ts) { + int ts_diff = (int)model->curr_ts - (int)model->generic->timestamp; + + canvas_draw_icon(canvas, 91, 46, &I_Timer_11x11); + + if(ts_diff > 60) { + int tmp_sec = ts_diff; + int cnt_min = 1; + for(int i = 1; tmp_sec > 60; i++) { + tmp_sec = tmp_sec - 60; + cnt_min = i; + } + + if(model->curr_ts % 2 == 0) { + canvas_draw_str_aligned(canvas, 105, 51, AlignLeft, AlignCenter, "Old"); + } else { + if(cnt_min >= 59) { + canvas_draw_str_aligned(canvas, 105, 51, AlignLeft, AlignCenter, "Old"); + } else { + snprintf(buffer, sizeof(buffer), "%dm", cnt_min); + canvas_draw_str_aligned(canvas, 114, 51, AlignCenter, AlignCenter, buffer); + } + } + + } else { + snprintf(buffer, sizeof(buffer), "%d", ts_diff); + canvas_draw_str_aligned(canvas, 112, 51, AlignCenter, AlignCenter, buffer); + } } } @@ -98,14 +134,19 @@ bool ws_view_receiver_info_input(InputEvent* event, void* context) { return true; } -void ws_view_receiver_info_enter(void* context) { - furi_assert(context); -} - -void ws_view_receiver_info_exit(void* context) { +static void ws_view_receiver_info_enter(void* context) { furi_assert(context); WSReceiverInfo* ws_receiver_info = context; + furi_timer_start(ws_receiver_info->timer, 1000); +} + +static void ws_view_receiver_info_exit(void* context) { + furi_assert(context); + WSReceiverInfo* ws_receiver_info = context; + + furi_timer_stop(ws_receiver_info->timer); + with_view_model( ws_receiver_info->view, WSReceiverInfoModel * model, @@ -113,6 +154,20 @@ void ws_view_receiver_info_exit(void* context) { false); } +static void ws_view_receiver_info_timer(void* context) { + WSReceiverInfo* ws_receiver_info = context; + // Force redraw + with_view_model( + ws_receiver_info->view, + WSReceiverInfoModel * model, + { + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + model->curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + }, + true); +} + WSReceiverInfo* ws_view_receiver_info_alloc() { WSReceiverInfo* ws_receiver_info = malloc(sizeof(WSReceiverInfo)); @@ -135,12 +190,17 @@ WSReceiverInfo* ws_view_receiver_info_alloc() { }, true); + ws_receiver_info->timer = + furi_timer_alloc(ws_view_receiver_info_timer, FuriTimerTypePeriodic, ws_receiver_info); + return ws_receiver_info; } void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info) { furi_assert(ws_receiver_info); + furi_timer_free(ws_receiver_info->timer); + with_view_model( ws_receiver_info->view, WSReceiverInfoModel * model, From 31a9a3f5f3dfe073857f5137e391eb3fc56e4e8d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 10 Dec 2022 20:31:07 +0300 Subject: [PATCH 06/21] SubGHz: Improve signal text visibility in history (#2111) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/subghz/views/receiver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 7aa699222..999eef6a5 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -8,7 +8,7 @@ #include #define FRAME_HEIGHT 12 -#define MAX_LEN_PX 100 +#define MAX_LEN_PX 111 #define MENU_ITEMS 4u #define UNLOCK_CNT 3 @@ -186,7 +186,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0); item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx); furi_string_set(str_buff, item_menu->item_str); - elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); + elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 7 : MAX_LEN_PX); if(model->idx == idx) { subghz_view_receiver_draw_frame(canvas, i, scrollbar); } else { From 2954ec6d97dbaa90974728a818008653d9b4ec88 Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Sat, 10 Dec 2022 21:05:52 +0300 Subject: [PATCH 07/21] [FL-3025] IR button overflow fix (#2115) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow for more than 255 button_menu items * Allow more than 255 items in submenu * Fix button_menu_reset Co-authored-by: あく --- .../services/gui/modules/button_menu.c | 19 +++--- applications/services/gui/modules/submenu.c | 58 ++++++++++--------- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/applications/services/gui/modules/button_menu.c b/applications/services/gui/modules/button_menu.c index ff12a9311..60bd160c5 100644 --- a/applications/services/gui/modules/button_menu.c +++ b/applications/services/gui/modules/button_menu.c @@ -30,7 +30,7 @@ struct ButtonMenu { typedef struct { ButtonMenuItemArray_t items; - uint8_t position; + size_t position; const char* header; } ButtonMenuModel; @@ -102,11 +102,9 @@ static void button_menu_view_draw_callback(Canvas* canvas, void* _model) { ButtonMenuModel* model = (ButtonMenuModel*)_model; canvas_set_font(canvas, FontSecondary); - uint8_t item_position = 0; - int8_t active_screen = model->position / BUTTONS_PER_SCREEN; - size_t items_size = ButtonMenuItemArray_size(model->items); - int8_t max_screen = ((int16_t)items_size - 1) / BUTTONS_PER_SCREEN; - ButtonMenuItemArray_it_t it; + const size_t active_screen = model->position / BUTTONS_PER_SCREEN; + const size_t items_size = ButtonMenuItemArray_size(model->items); + const size_t max_screen = items_size ? (items_size - 1) / BUTTONS_PER_SCREEN : 0; if(active_screen > 0) { canvas_draw_icon(canvas, 28, 1, &I_InfraredArrowUp_4x8); @@ -125,6 +123,9 @@ static void button_menu_view_draw_callback(Canvas* canvas, void* _model) { furi_string_free(disp_str); } + size_t item_position = 0; + ButtonMenuItemArray_it_t it; + for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it); ButtonMenuItemArray_next(it), ++item_position) { if(active_screen == (item_position / BUTTONS_PER_SCREEN)) { @@ -195,14 +196,14 @@ static void button_menu_process_ok(ButtonMenu* button_menu, InputType type) { if(item) { if(item->type == ButtonMenuItemTypeControl) { if(type == InputTypeShort) { - if(item && item->callback) { + if(item->callback) { item->callback(item->callback_context, item->index, type); } } } if(item->type == ButtonMenuItemTypeCommon) { if((type == InputTypePress) || (type == InputTypeRelease)) { - if(item && item->callback) { + if(item->callback) { item->callback(item->callback_context, item->index, type); } } @@ -341,7 +342,7 @@ void button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index) { button_menu->view, ButtonMenuModel * model, { - uint8_t item_position = 0; + size_t item_position = 0; ButtonMenuItemArray_it_t it; for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it); ButtonMenuItemArray_next(it), ++item_position) { diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index 949598772..b7152a3d2 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -20,8 +20,8 @@ ARRAY_DEF(SubmenuItemArray, SubmenuItem, M_POD_OPLIST); typedef struct { SubmenuItemArray_t items; const char* header; - uint8_t position; - uint8_t window_position; + size_t position; + size_t window_position; } SubmenuModel; static void submenu_process_up(Submenu* submenu); @@ -36,19 +36,19 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { canvas_clear(canvas); - uint8_t position = 0; - SubmenuItemArray_it_t it; - if(model->header) { canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 4, 11, model->header); } canvas_set_font(canvas, FontSecondary); + + size_t position = 0; + SubmenuItemArray_it_t it; for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); SubmenuItemArray_next(it)) { - uint8_t item_position = position - model->window_position; - uint8_t items_on_screen = model->header ? 3 : 4; + const size_t item_position = position - model->window_position; + const size_t items_on_screen = model->header ? 3 : 4; uint8_t y_offset = model->header ? 16 : 0; if(item_position < items_on_screen) { @@ -198,7 +198,7 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) { submenu->view, SubmenuModel * model, { - uint32_t position = 0; + size_t position = 0; SubmenuItemArray_it_t it; for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); SubmenuItemArray_next(it)) { @@ -208,7 +208,9 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) { position++; } - if(position >= SubmenuItemArray_size(model->items)) { + const size_t items_size = SubmenuItemArray_size(model->items); + + if(position >= items_size) { position = 0; } @@ -219,16 +221,12 @@ void submenu_set_selected_item(Submenu* submenu, uint32_t index) { model->window_position -= 1; } - uint8_t items_on_screen = model->header ? 3 : 4; + const size_t items_on_screen = model->header ? 3 : 4; - if(SubmenuItemArray_size(model->items) <= items_on_screen) { + if(items_size <= items_on_screen) { model->window_position = 0; - } else { - if(model->window_position >= - (SubmenuItemArray_size(model->items) - items_on_screen)) { - model->window_position = - (SubmenuItemArray_size(model->items) - items_on_screen); - } + } else if(model->window_position >= items_size - items_on_screen) { + model->window_position = items_size - items_on_screen; } }, true); @@ -239,16 +237,18 @@ void submenu_process_up(Submenu* submenu) { submenu->view, SubmenuModel * model, { - uint8_t items_on_screen = model->header ? 3 : 4; + const size_t items_on_screen = model->header ? 3 : 4; + const size_t items_size = SubmenuItemArray_size(model->items); + if(model->position > 0) { model->position--; - if(((model->position - model->window_position) < 1) && - model->window_position > 0) { + if((model->position - model->window_position < 1) && + (model->window_position > 0)) { model->window_position--; } } else { - model->position = SubmenuItemArray_size(model->items) - 1; - if(model->position > (items_on_screen - 1)) { + model->position = items_size - 1; + if(model->position > items_on_screen - 1) { model->window_position = model->position - (items_on_screen - 1); } } @@ -261,12 +261,13 @@ void submenu_process_down(Submenu* submenu) { submenu->view, SubmenuModel * model, { - uint8_t items_on_screen = model->header ? 3 : 4; - if(model->position < (SubmenuItemArray_size(model->items) - 1)) { + const size_t items_on_screen = model->header ? 3 : 4; + const size_t items_size = SubmenuItemArray_size(model->items); + + if(model->position < items_size - 1) { model->position++; - if((model->position - model->window_position) > (items_on_screen - 2) && - model->window_position < - (SubmenuItemArray_size(model->items) - items_on_screen)) { + if((model->position - model->window_position > items_on_screen - 2) && + (model->window_position < items_size - items_on_screen)) { model->window_position++; } } else { @@ -284,7 +285,8 @@ void submenu_process_ok(Submenu* submenu) { submenu->view, SubmenuModel * model, { - if(model->position < (SubmenuItemArray_size(model->items))) { + const size_t items_size = SubmenuItemArray_size(model->items); + if(model->position < items_size) { item = SubmenuItemArray_get(model->items, model->position); } }, From 27921e42ff65e07e94f7c7cad08d0c2699602e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Sun, 11 Dec 2022 05:34:12 +0900 Subject: [PATCH 08/21] Github: fix unit tests workflow (#2117) --- .github/workflows/unit_tests.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 4eab21c87..308ec5929 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -11,6 +11,12 @@ jobs: run_units_on_test_bench: runs-on: [self-hosted, FlipperZeroTest] steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + - name: Checkout code uses: actions/checkout@v3 with: From 6ff437ad1824cc1b74477a32ae8b98a071c883fb Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sun, 11 Dec 2022 09:32:25 -0800 Subject: [PATCH 09/21] Dictionary stuff: iClass keys (#2118) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * MFC comments * iClass keys from proxmark3 repo Co-authored-by: あく --- .../resources/nfc/assets/mf_classic_dict.nfc | 5 +- .../picopass/assets/iclass_elite_dict.txt | 47 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 assets/resources/picopass/assets/iclass_elite_dict.txt diff --git a/assets/resources/nfc/assets/mf_classic_dict.nfc b/assets/resources/nfc/assets/mf_classic_dict.nfc index f4cdf5952..0925888f2 100644 --- a/assets/resources/nfc/assets/mf_classic_dict.nfc +++ b/assets/resources/nfc/assets/mf_classic_dict.nfc @@ -244,6 +244,7 @@ FEE470A4CB58 1F1A0A111B5B 1F1FFE000000 2031D1E57A3B +# HID Key B 204752454154 21A600056CB0 22729A9BD40F @@ -292,6 +293,7 @@ FEE470A4CB58 45635EF66EF3 476242304C53 484558414354 +# HID Key A 484944204953 484A57696F4A 48734389EDC3 @@ -1226,6 +1228,7 @@ C41514DEFC07 ABFEDC124578 046154274C11 5429D67E1F57 +# SMARTair Key B E7316853E731 CD7FFFF81C4A F253C30568C4 @@ -1308,4 +1311,4 @@ CE99FBC8BD26 # PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K) 009FB42D98ED -002E626E2820 \ No newline at end of file +002E626E2820 diff --git a/assets/resources/picopass/assets/iclass_elite_dict.txt b/assets/resources/picopass/assets/iclass_elite_dict.txt new file mode 100644 index 000000000..46808ef60 --- /dev/null +++ b/assets/resources/picopass/assets/iclass_elite_dict.txt @@ -0,0 +1,47 @@ + +## From https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/iclass_default_keys.dic + +# AA1 +AEA684A6DAB23278 +# key1/Kc from PicoPass 2k documentation +7665544332211000 +# SAGEM +0123456789ABCDEF +# from loclass demo file. +5b7c62c491c11b39 +# Kd from PicoPass 2k documentation +F0E1D2C3B4A59687 +# PicoPass Default Exchange Key +5CBCF1DA45D5FB4F +# From HID multiclassSE reader +31ad7ebd2f282168 +# From pastebin: https://pastebin.com/uHqpjiuU +6EFD46EFCBB3C875 +E033CA419AEE43F9 + +# iCopy-x DRM keys +# iCL tags +2020666666668888 +# iCS tags reversed from the SOs +6666202066668888 + +# default picopass KD / Page 0 / Book 1 +FDCB5A52EA8F3090 +237FF9079863DF44 +5ADC25FB27181D32 +83B881F2936B2E49 +43644E61EE866BA5 +897034143D016080 +82D17B44C0122963 +4895CA7DE65E2025 +DADAD4C57BE271B7 +E41E9EDEF5719ABF +293D275EC3AF9C7F +C3C169251B8A70FB +F41DAF58B20C8B91 +28877A609EC0DD2B +66584C91EE80D5E5 +C1B74D7478053AE2 + +# default iCLASS RFIDeas +6B65797374726B72 From 87fb852bcfafe5f2113348827159acb3fa0bc243 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 11 Dec 2022 22:29:58 +0300 Subject: [PATCH 10/21] Weather Station: Fix display of temps lower than -9 (#2120) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../views/weather_station_receiver_info.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.c b/applications/plugins/weather_station/views/weather_station_receiver_info.c index c2fa00641..058099217 100644 --- a/applications/plugins/weather_station/views/weather_station_receiver_info.c +++ b/applications/plugins/weather_station/views/weather_station_receiver_info.c @@ -82,8 +82,14 @@ void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) { if(model->generic->temp != WS_NO_TEMPERATURE) { canvas_draw_icon(canvas, 6, 43, &I_Therm_7x16); snprintf(buffer, sizeof(buffer), "%3.1f C", (double)model->generic->temp); - canvas_draw_str_aligned(canvas, 47, 47, AlignRight, AlignTop, buffer); - canvas_draw_circle(canvas, 38, 46, 1); + uint8_t temp_x1 = 47; + uint8_t temp_x2 = 38; + if(model->generic->temp < -9.0) { + temp_x1 = 49; + temp_x2 = 40; + } + canvas_draw_str_aligned(canvas, temp_x1, 47, AlignRight, AlignTop, buffer); + canvas_draw_circle(canvas, temp_x2, 46, 1); } if(model->generic->humidity != WS_NO_HUMIDITY) { From d541f142c8af39313b525aa28fc7aa7923c0c3f6 Mon Sep 17 00:00:00 2001 From: Kassim Date: Mon, 12 Dec 2022 09:47:22 +0000 Subject: [PATCH 11/21] Add Mouse Jiggler to HID Remote (#2113) * feat: add Mouse Jiggler to HID Remote * move processing to use furi_timer instead of draw loop * HidApp: refine mouse jiggler, move timer work into enter/exit callbacks Co-authored-by: Aleksandr Kutuzov --- applications/plugins/hid_app/hid.c | 29 +++- applications/plugins/hid_app/hid.h | 2 + applications/plugins/hid_app/views.h | 1 + .../plugins/hid_app/views/hid_mouse_jiggler.c | 149 ++++++++++++++++++ .../plugins/hid_app/views/hid_mouse_jiggler.h | 17 ++ 5 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 applications/plugins/hid_app/views/hid_mouse_jiggler.c create mode 100644 applications/plugins/hid_app/views/hid_mouse_jiggler.h diff --git a/applications/plugins/hid_app/hid.c b/applications/plugins/hid_app/hid.c index 2a617fdea..d205d96eb 100644 --- a/applications/plugins/hid_app/hid.c +++ b/applications/plugins/hid_app/hid.c @@ -9,10 +9,10 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexKeynote, HidSubmenuIndexKeyboard, HidSubmenuIndexMedia, - BtHidSubmenuIndexTikTok, + HidSubmenuIndexTikTok, HidSubmenuIndexMouse, + HidSubmenuIndexMouseJiggler, }; -typedef enum { ConnTypeSubmenuIndexBluetooth, ConnTypeSubmenuIndexUsb } ConnTypeDebugSubmenuIndex; static void hid_submenu_callback(void* context, uint32_t index) { furi_assert(context); @@ -29,9 +29,12 @@ static void hid_submenu_callback(void* context, uint32_t index) { } else if(index == HidSubmenuIndexMouse) { app->view_id = HidViewMouse; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse); - } else if(index == BtHidSubmenuIndexTikTok) { + } else if(index == HidSubmenuIndexTikTok) { app->view_id = BtHidViewTikTok; view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); + } else if(index == HidSubmenuIndexMouseJiggler) { + app->view_id = HidViewMouseJiggler; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); } } @@ -48,6 +51,7 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con hid_keyboard_set_connected_status(hid->hid_keyboard, connected); hid_media_set_connected_status(hid->hid_media, connected); hid_mouse_set_connected_status(hid->hid_mouse, connected); + hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); hid_tiktok_set_connected_status(hid->hid_tiktok, connected); } @@ -104,10 +108,16 @@ Hid* hid_alloc(HidTransport transport) { submenu_add_item( app->device_type_submenu, "TikTok Controller", - BtHidSubmenuIndexTikTok, + HidSubmenuIndexTikTok, hid_submenu_callback, app); } + submenu_add_item( + app->device_type_submenu, + "Mouse Jiggler", + HidSubmenuIndexMouseJiggler, + hid_submenu_callback, + app); view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit); view_dispatcher_add_view( app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu)); @@ -160,6 +170,15 @@ Hid* hid_app_alloc_view(void* context) { view_dispatcher_add_view( app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); + // Mouse jiggler view + app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); + view_set_previous_callback( + hid_mouse_jiggler_get_view(app->hid_mouse_jiggler), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, + HidViewMouseJiggler, + hid_mouse_jiggler_get_view(app->hid_mouse_jiggler)); + return app; } @@ -182,6 +201,8 @@ void hid_free(Hid* app) { hid_media_free(app->hid_media); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); hid_mouse_free(app->hid_mouse); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler); + hid_mouse_jiggler_free(app->hid_mouse_jiggler); view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); hid_tiktok_free(app->hid_tiktok); view_dispatcher_free(app->view_dispatcher); diff --git a/applications/plugins/hid_app/hid.h b/applications/plugins/hid_app/hid.h index 81ebcf566..375a89706 100644 --- a/applications/plugins/hid_app/hid.h +++ b/applications/plugins/hid_app/hid.h @@ -19,6 +19,7 @@ #include "views/hid_keyboard.h" #include "views/hid_media.h" #include "views/hid_mouse.h" +#include "views/hid_mouse_jiggler.h" #include "views/hid_tiktok.h" typedef enum { @@ -39,6 +40,7 @@ struct Hid { HidKeyboard* hid_keyboard; HidMedia* hid_media; HidMouse* hid_mouse; + HidMouseJiggler* hid_mouse_jiggler; HidTikTok* hid_tiktok; HidTransport transport; diff --git a/applications/plugins/hid_app/views.h b/applications/plugins/hid_app/views.h index 68a827ad6..2a44832e1 100644 --- a/applications/plugins/hid_app/views.h +++ b/applications/plugins/hid_app/views.h @@ -4,6 +4,7 @@ typedef enum { HidViewKeyboard, HidViewMedia, HidViewMouse, + HidViewMouseJiggler, BtHidViewTikTok, HidViewExitConfirm, } HidView; \ No newline at end of file diff --git a/applications/plugins/hid_app/views/hid_mouse_jiggler.c b/applications/plugins/hid_app/views/hid_mouse_jiggler.c new file mode 100644 index 000000000..a2b07c7a1 --- /dev/null +++ b/applications/plugins/hid_app/views/hid_mouse_jiggler.c @@ -0,0 +1,149 @@ +#include "hid_mouse_jiggler.h" +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMouseJiggler" + +struct HidMouseJiggler { + View* view; + Hid* hid; + FuriTimer* timer; +}; + +typedef struct { + bool connected; + bool running; + uint8_t counter; +} HidMouseJigglerModel; + +static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMouseJigglerModel* model = context; + + // Header + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Jiggler"); + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto jiggle"); + canvas_set_font(canvas, FontSecondary); + + // Ok + canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); + if(model->running) { + elements_slightly_rounded_box(canvas, 66, 27, 60, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); + if(model->running) { + elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop"); + } else { + elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start"); + } + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit"); +} + +static void hid_mouse_jiggler_timer_callback(void* context) { + furi_assert(context); + HidMouseJiggler* hid_mouse_jiggler = context; + with_view_model( + hid_mouse_jiggler->view, + HidMouseJigglerModel * model, + { + if(model->running) { + model->counter++; + hid_hal_mouse_move( + hid_mouse_jiggler->hid, + (model->counter % 2 == 0) ? MOUSE_MOVE_SHORT : -MOUSE_MOVE_SHORT, + 0); + } + }, + false); +} + +static void hid_mouse_jiggler_enter_callback(void* context) { + furi_assert(context); + HidMouseJiggler* hid_mouse_jiggler = context; + + furi_timer_start(hid_mouse_jiggler->timer, 500); +} + +static void hid_mouse_jiggler_exit_callback(void* context) { + furi_assert(context); + HidMouseJiggler* hid_mouse_jiggler = context; + furi_timer_stop(hid_mouse_jiggler->timer); +} + +static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMouseJiggler* hid_mouse_jiggler = context; + + bool consumed = false; + + if(event->key == InputKeyOk) { + with_view_model( + hid_mouse_jiggler->view, + HidMouseJigglerModel * model, + { model->running = !model->running; }, + true); + consumed = true; + } + + return consumed; +} + +HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) { + HidMouseJiggler* hid_mouse_jiggler = malloc(sizeof(HidMouseJiggler)); + + hid_mouse_jiggler->view = view_alloc(); + view_set_context(hid_mouse_jiggler->view, hid_mouse_jiggler); + view_allocate_model( + hid_mouse_jiggler->view, ViewModelTypeLocking, sizeof(HidMouseJigglerModel)); + view_set_draw_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_draw_callback); + view_set_input_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_input_callback); + view_set_enter_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_enter_callback); + view_set_exit_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_exit_callback); + + hid_mouse_jiggler->hid = hid; + + hid_mouse_jiggler->timer = furi_timer_alloc( + hid_mouse_jiggler_timer_callback, FuriTimerTypePeriodic, hid_mouse_jiggler); + + return hid_mouse_jiggler; +} + +void hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler) { + furi_assert(hid_mouse_jiggler); + + furi_timer_stop(hid_mouse_jiggler->timer); + furi_timer_free(hid_mouse_jiggler->timer); + + view_free(hid_mouse_jiggler->view); + + free(hid_mouse_jiggler); +} + +View* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler) { + furi_assert(hid_mouse_jiggler); + return hid_mouse_jiggler->view; +} + +void hid_mouse_jiggler_set_connected_status(HidMouseJiggler* hid_mouse_jiggler, bool connected) { + furi_assert(hid_mouse_jiggler); + with_view_model( + hid_mouse_jiggler->view, + HidMouseJigglerModel * model, + { model->connected = connected; }, + true); +} diff --git a/applications/plugins/hid_app/views/hid_mouse_jiggler.h b/applications/plugins/hid_app/views/hid_mouse_jiggler.h new file mode 100644 index 000000000..0813b4351 --- /dev/null +++ b/applications/plugins/hid_app/views/hid_mouse_jiggler.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#define MOUSE_MOVE_SHORT 5 +#define MOUSE_MOVE_LONG 20 + +typedef struct Hid Hid; +typedef struct HidMouseJiggler HidMouseJiggler; + +HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* bt_hid); + +void hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler); + +View* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler); + +void hid_mouse_jiggler_set_connected_status(HidMouseJiggler* hid_mouse_jiggler, bool connected); From 1c12613863d31bfd778764318742ea28039948c9 Mon Sep 17 00:00:00 2001 From: usiegl00 <50933431+usiegl00@users.noreply.github.com> Date: Mon, 12 Dec 2022 21:46:41 +0900 Subject: [PATCH 12/21] Prevent hacking related backgrounds from being displayed in dummy mode. (#2107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Prevent hacking related backgrounds from being displayed in dummy mode. * Add function call to animation manager to set dummy mode. * Reboot retains dummy mode background. Co-authored-by: あく --- .../services/desktop/animations/animation_manager.c | 11 ++++++++++- .../services/desktop/animations/animation_manager.h | 8 ++++++++ applications/services/desktop/desktop.c | 3 +++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 36c5b3975..9c22d1314 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -52,6 +52,7 @@ struct AnimationManager { FuriString* freezed_animation_name; int32_t freezed_animation_time_left; ViewStack* view_stack; + bool dummy_mode; }; static StorageAnimation* @@ -93,6 +94,12 @@ void animation_manager_set_interact_callback( animation_manager->interact_callback = callback; } +void animation_manager_set_dummy_mode_state(AnimationManager* animation_manager, bool enabled) { + furi_assert(animation_manager); + animation_manager->dummy_mode = enabled; + animation_manager_start_new_idle(animation_manager); +} + static void animation_manager_check_blocking_callback(const void* message, void* context) { const StorageEvent* storage_event = message; @@ -363,7 +370,9 @@ static bool animation_manager_is_valid_idle_animation( static StorageAnimation* animation_manager_select_idle_animation(AnimationManager* animation_manager) { - UNUSED(animation_manager); + if(animation_manager->dummy_mode) { + return animation_storage_find_animation(HARDCODED_ANIMATION_NAME); + } StorageAnimationList_t animation_list; StorageAnimationList_init(animation_list); animation_storage_fill_animation_list(&animation_list); diff --git a/applications/services/desktop/animations/animation_manager.h b/applications/services/desktop/animations/animation_manager.h index 234d20de0..a3dcc8827 100644 --- a/applications/services/desktop/animations/animation_manager.h +++ b/applications/services/desktop/animations/animation_manager.h @@ -157,3 +157,11 @@ void animation_manager_unload_and_stall_animation(AnimationManager* animation_ma * @animation_manager instance */ void animation_manager_load_and_continue_animation(AnimationManager* animation_manager); + +/** + * Enable or disable dummy mode backgrounds of animation manager. + * + * @animation_manager instance + * @enabled bool + */ +void animation_manager_set_dummy_mode_state(AnimationManager* animation_manager, bool enabled); diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index b45a9d621..848f5cb63 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -144,6 +144,7 @@ void desktop_unlock(Desktop* desktop) { void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) { view_port_enabled_set(desktop->dummy_mode_icon_viewport, enabled); desktop_main_set_dummy_mode_state(desktop->main_view, enabled); + animation_manager_set_dummy_mode_state(desktop->animation_manager, enabled); desktop->settings.dummy_mode = enabled; DESKTOP_SETTINGS_SAVE(&desktop->settings); } @@ -330,6 +331,8 @@ int32_t desktop_srv(void* p) { view_port_enabled_set(desktop->dummy_mode_icon_viewport, desktop->settings.dummy_mode); desktop_main_set_dummy_mode_state(desktop->main_view, desktop->settings.dummy_mode); + animation_manager_set_dummy_mode_state( + desktop->animation_manager, desktop->settings.dummy_mode); scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); From 1fa4c646e69d3d91f3cea9d323a71dbfe1e18c42 Mon Sep 17 00:00:00 2001 From: Der Skythe <31771569+derskythe@users.noreply.github.com> Date: Wed, 14 Dec 2022 11:42:13 +0400 Subject: [PATCH 13/21] VSCode: add task 'Serial console' and group task with sequence calling (#2121) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add task 'Serial console' and group task with sequence calling * PR fixes Co-authored-by: あく --- .vscode/example/tasks.json | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json index 9baaf97b4..c16c3ab4f 100644 --- a/.vscode/example/tasks.json +++ b/.vscode/example/tasks.json @@ -128,6 +128,38 @@ "group": "build", "type": "shell", "command": "./fbt COMPACT=1 DEBUG=0 launch_app APPSRC=${relativeFileDirname}" + }, + { + "label": "[Debug] Launch App on Flipper with Serial Console", + "dependsOrder": "sequence", + "group": "build", + "dependsOn": [ + "[Debug] Launch App on Flipper", + "Serial Console" + ] + }, + { + // Press Ctrl+] to quit + "label": "Serial Console", + "type": "shell", + "command": "./fbt cli", + "group": "none", + "isBackground": true, + "options": { + "env": { + "FBT_NO_SYNC": "0" + } + }, + "presentation": { + "reveal": "always", + "revealProblems": "never", + "showReuseMessage": false, + "panel": "dedicated", + "focus": true, + "echo": true, + "close": true, + "group": "Logger" + } } ] -} \ No newline at end of file +} From 327df4a8130dd389ab39186e8a7c1640d51be705 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 14 Dec 2022 12:27:55 +0400 Subject: [PATCH 14/21] [FL-3034] WS: fix protocol and add new (#2116) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WS: fix Nexus-TH potocol * WS: add Oregon_v1 protocol * WS: add AmbientWeather-TX8300 protocol Co-authored-by: あく --- .../helpers/weather_station_types.h | 2 +- .../weather_station/protocols/nexus_th.c | 4 + .../weather_station/protocols/oregon_v1.c | 331 ++++++++++++++++++ .../weather_station/protocols/oregon_v1.h | 79 +++++ .../protocols/protocol_items.c | 2 + .../protocols/protocol_items.h | 2 + .../weather_station/protocols/tx_8300.c | 293 ++++++++++++++++ .../weather_station/protocols/tx_8300.h | 79 +++++ 8 files changed, 791 insertions(+), 1 deletion(-) create mode 100644 applications/plugins/weather_station/protocols/oregon_v1.c create mode 100644 applications/plugins/weather_station/protocols/oregon_v1.h create mode 100644 applications/plugins/weather_station/protocols/tx_8300.c create mode 100644 applications/plugins/weather_station/protocols/tx_8300.h diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h index a23540e3d..16c195fa1 100644 --- a/applications/plugins/weather_station/helpers/weather_station_types.h +++ b/applications/plugins/weather_station/helpers/weather_station_types.h @@ -3,7 +3,7 @@ #include #include -#define WS_VERSION_APP "0.5" +#define WS_VERSION_APP "0.6" #define WS_DEVELOPED "SkorP" #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" diff --git a/applications/plugins/weather_station/protocols/nexus_th.c b/applications/plugins/weather_station/protocols/nexus_th.c index 7d4a77aea..38f2fe895 100644 --- a/applications/plugins/weather_station/protocols/nexus_th.c +++ b/applications/plugins/weather_station/protocols/nexus_th.c @@ -135,6 +135,10 @@ static void ws_protocol_nexus_th_remote_controller(WSBlockGeneric* instance) { } instance->humidity = instance->data & 0xFF; + if(instance->humidity > 95) + instance->humidity = 95; + else if(instance->humidity < 20) + instance->humidity = 20; } void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration) { diff --git a/applications/plugins/weather_station/protocols/oregon_v1.c b/applications/plugins/weather_station/protocols/oregon_v1.c new file mode 100644 index 000000000..d1cc4c7a7 --- /dev/null +++ b/applications/plugins/weather_station/protocols/oregon_v1.c @@ -0,0 +1,331 @@ +#include "oregon_v1.h" +#include + +#define TAG "WSProtocolOregon_V1" + +/* + * Help + * https://github.dev/merbanan/rtl_433/blob/bb1be7f186ac0fdb7dc5d77693847d96fb95281e/src/devices/oregon_scientific_v1.c + * + * OSv1 protocol. + * + * MC with nominal bit width of 2930 us. + * Pulses are somewhat longer than nominal half-bit width, 1748 us / 3216 us, + * Gaps are somewhat shorter than nominal half-bit width, 1176 us / 2640 us. + * After 12 preamble bits there is 4200 us gap, 5780 us pulse, 5200 us gap. + * And next 32 bit data + * + * Care must be taken with the gap after the sync pulse since it + * is outside of the normal clocking. Because of this a data stream + * beginning with a 0 will have data in this gap. + * + * + * Data is in reverse order of bits + * RevBit(data32bit)=> tib23atad + * + * tib23atad => xxxxxxxx | busuTTTT | ttttzzzz | ccuuiiii + * + * - i: ID + * - x: CRC; + * - u: unknown; + * - b: battery low; flag to indicate low battery voltage + * - s: temperature sign + * - T: BCD, Temperature; in C * 10 + * - t: BCD, Temperature; in C * 1 + * - z: BCD, Temperature; in C * 0.1 + * - c: Channel 00=CH1, 01=CH2, 10=CH3 + * + */ + +#define OREGON_V1_HEADER_OK 0xFF + +static const SubGhzBlockConst ws_protocol_oregon_v1_const = { + .te_short = 1465, + .te_long = 2930, + .te_delta = 350, + .min_count_bit_for_found = 32, +}; + +struct WSProtocolDecoderOregon_V1 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + ManchesterState manchester_state; + uint16_t header_count; + uint8_t first_bit; +}; + +struct WSProtocolEncoderOregon_V1 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + Oregon_V1DecoderStepReset = 0, + Oregon_V1DecoderStepFoundPreamble, + Oregon_V1DecoderStepParse, +} Oregon_V1DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_oregon_v1_decoder = { + .alloc = ws_protocol_decoder_oregon_v1_alloc, + .free = ws_protocol_decoder_oregon_v1_free, + + .feed = ws_protocol_decoder_oregon_v1_feed, + .reset = ws_protocol_decoder_oregon_v1_reset, + + .get_hash_data = ws_protocol_decoder_oregon_v1_get_hash_data, + .serialize = ws_protocol_decoder_oregon_v1_serialize, + .deserialize = ws_protocol_decoder_oregon_v1_deserialize, + .get_string = ws_protocol_decoder_oregon_v1_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_oregon_v1_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_oregon_v1 = { + .name = WS_PROTOCOL_OREGON_V1_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_oregon_v1_decoder, + .encoder = &ws_protocol_oregon_v1_encoder, +}; + +void* ws_protocol_decoder_oregon_v1_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderOregon_V1* instance = malloc(sizeof(WSProtocolDecoderOregon_V1)); + instance->base.protocol = &ws_protocol_oregon_v1; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_oregon_v1_free(void* context) { + furi_assert(context); + WSProtocolDecoderOregon_V1* instance = context; + free(instance); +} + +void ws_protocol_decoder_oregon_v1_reset(void* context) { + furi_assert(context); + WSProtocolDecoderOregon_V1* instance = context; + instance->decoder.parser_step = Oregon_V1DecoderStepReset; +} + +static bool ws_protocol_oregon_v1_check(WSProtocolDecoderOregon_V1* instance) { + if(!instance->decoder.decode_data) return false; + uint64_t data = subghz_protocol_blocks_reverse_key(instance->decoder.decode_data, 32); + uint16_t crc = (data & 0xff) + ((data >> 8) & 0xff) + ((data >> 16) & 0xff); + crc = (crc & 0xff) + ((crc >> 8) & 0xff); + return (crc == ((data >> 24) & 0xFF)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_oregon_v1_remote_controller(WSBlockGeneric* instance) { + uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 32); + + instance->id = data & 0xFF; + instance->channel = ((data >> 6) & 0x03) + 1; + + float temp_raw = + ((data >> 8) & 0x0F) * 0.1f + ((data >> 12) & 0x0F) + ((data >> 16) & 0x0F) * 10.0f; + if(!((data >> 21) & 1)) { + instance->temp = temp_raw; + } else { + instance->temp = -temp_raw; + } + + instance->battery_low = !(instance->data >> 23) & 1; + + instance->btn = WS_NO_BTN; + instance->humidity = WS_NO_HUMIDITY; +} + +void ws_protocol_decoder_oregon_v1_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderOregon_V1* instance = context; + ManchesterEvent event = ManchesterEventReset; + switch(instance->decoder.parser_step) { + case Oregon_V1DecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) < + ws_protocol_oregon_v1_const.te_delta)) { + instance->decoder.parser_step = Oregon_V1DecoderStepFoundPreamble; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + case Oregon_V1DecoderStepFoundPreamble: + if(level) { + //keep high levels, if they suit our durations + if((DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) < + ws_protocol_oregon_v1_const.te_delta) || + (DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short * 4) < + ws_protocol_oregon_v1_const.te_delta)) { + instance->decoder.te_last = duration; + } else { + instance->decoder.parser_step = Oregon_V1DecoderStepReset; + } + } else if( + //checking low levels + (DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) < + ws_protocol_oregon_v1_const.te_delta) && + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_oregon_v1_const.te_short) < + ws_protocol_oregon_v1_const.te_delta)) { + // Found header + instance->header_count++; + } else if( + (DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short * 3) < + ws_protocol_oregon_v1_const.te_delta) && + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_oregon_v1_const.te_short) < + ws_protocol_oregon_v1_const.te_delta)) { + // check header + if(instance->header_count > 7) { + instance->header_count = OREGON_V1_HEADER_OK; + } + } else if( + (instance->header_count == OREGON_V1_HEADER_OK) && + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_oregon_v1_const.te_short * 4) < + ws_protocol_oregon_v1_const.te_delta)) { + //found all the necessary patterns + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 1; + manchester_advance( + instance->manchester_state, + ManchesterEventReset, + &instance->manchester_state, + NULL); + instance->decoder.parser_step = Oregon_V1DecoderStepParse; + if(duration < ws_protocol_oregon_v1_const.te_short * 4) { + instance->first_bit = 1; + } else { + instance->first_bit = 0; + } + } else { + instance->decoder.parser_step = Oregon_V1DecoderStepReset; + } + break; + case Oregon_V1DecoderStepParse: + if(level) { + if(DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) < + ws_protocol_oregon_v1_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_long) < + ws_protocol_oregon_v1_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = Oregon_V1DecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) < + ws_protocol_oregon_v1_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_long) < + ws_protocol_oregon_v1_const.te_delta) { + event = ManchesterEventLongLow; + } else if(duration >= ((uint32_t)ws_protocol_oregon_v1_const.te_long * 2)) { + if(instance->decoder.decode_count_bit == + ws_protocol_oregon_v1_const.min_count_bit_for_found) { + if(instance->first_bit) { + instance->decoder.decode_data = ~instance->decoder.decode_data | (1 << 31); + } + if(ws_protocol_oregon_v1_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_oregon_v1_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_state, + ManchesterEventReset, + &instance->manchester_state, + NULL); + } else { + instance->decoder.parser_step = Oregon_V1DecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_state, event, &instance->manchester_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data; + instance->decoder.decode_count_bit++; + } + } + + break; + } +} + +uint8_t ws_protocol_decoder_oregon_v1_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderOregon_V1* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_oregon_v1_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderOregon_V1* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_oregon_v1_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderOregon_V1* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_oregon_v1_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_oregon_v1_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderOregon_V1* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/oregon_v1.h b/applications/plugins/weather_station/protocols/oregon_v1.h new file mode 100644 index 000000000..c9aa5af48 --- /dev/null +++ b/applications/plugins/weather_station/protocols/oregon_v1.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_OREGON_V1_NAME "Oregon-v1" + +typedef struct WSProtocolDecoderOregon_V1 WSProtocolDecoderOregon_V1; +typedef struct WSProtocolEncoderOregon_V1 WSProtocolEncoderOregon_V1; + +extern const SubGhzProtocolDecoder ws_protocol_oregon_v1_decoder; +extern const SubGhzProtocolEncoder ws_protocol_oregon_v1_encoder; +extern const SubGhzProtocol ws_protocol_oregon_v1; + +/** + * Allocate WSProtocolDecoderOregon_V1. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderOregon_V1* pointer to a WSProtocolDecoderOregon_V1 instance + */ +void* ws_protocol_decoder_oregon_v1_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderOregon_V1. + * @param context Pointer to a WSProtocolDecoderOregon_V1 instance + */ +void ws_protocol_decoder_oregon_v1_free(void* context); + +/** + * Reset decoder WSProtocolDecoderOregon_V1. + * @param context Pointer to a WSProtocolDecoderOregon_V1 instance + */ +void ws_protocol_decoder_oregon_v1_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderOregon_V1 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_oregon_v1_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderOregon_V1 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_oregon_v1_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderOregon_V1. + * @param context Pointer to a WSProtocolDecoderOregon_V1 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_oregon_v1_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderOregon_V1. + * @param context Pointer to a WSProtocolDecoderOregon_V1 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_oregon_v1_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderOregon_V1 instance + * @param output Resulting text + */ +void ws_protocol_decoder_oregon_v1_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/plugins/weather_station/protocols/protocol_items.c index 9ad8ce1ac..99c8344f4 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.c +++ b/applications/plugins/weather_station/protocols/protocol_items.c @@ -13,6 +13,8 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_acurite_592txr, &ws_protocol_ambient_weather, &ws_protocol_auriol_th, + &ws_protocol_oregon_v1, + &ws_protocol_tx_8300, }; const SubGhzProtocolRegistry weather_station_protocol_registry = { diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/plugins/weather_station/protocols/protocol_items.h index 4fef89442..9d5d096f8 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.h +++ b/applications/plugins/weather_station/protocols/protocol_items.h @@ -13,5 +13,7 @@ #include "acurite_592txr.h" #include "ambient_weather.h" #include "auriol_hg0601a.h" +#include "oregon_v1.h" +#include "tx_8300.h" extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/applications/plugins/weather_station/protocols/tx_8300.c b/applications/plugins/weather_station/protocols/tx_8300.c new file mode 100644 index 000000000..ee0412ba9 --- /dev/null +++ b/applications/plugins/weather_station/protocols/tx_8300.c @@ -0,0 +1,293 @@ +#include "tx_8300.h" + +#define TAG "WSProtocolTX_8300" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/ambientweather_tx8300.c + * + * Ambient Weather TX-8300 (also sold as TFA 30.3211.02). + * 1970us pulse with variable gap (third pulse 3920 us). + * Above 79% humidity, gap after third pulse is 5848 us. + * - Bit 1 : 1970us pulse with 3888 us gap + * - Bit 0 : 1970us pulse with 1936 us gap + * 74 bit (2 bit preamble and 72 bit data => 9 bytes => 18 nibbles) + * The preamble seems to be a repeat counter (00, and 01 seen), + * the first 4 bytes are data, + * the second 4 bytes the same data inverted, + * the last byte is a checksum. + * Preamble format (2 bits): + * [1 bit (0)] [1 bit rolling count] + * Payload format (32 bits): + * HHHHhhhh ??CCNIII IIIITTTT ttttuuuu + * - H = First BCD digit humidity (the MSB might be distorted by the demod) + * - h = Second BCD digit humidity, invalid humidity seems to be 0x0e + * - ? = Likely battery flag, 2 bits + * - C = Channel, 2 bits + * - N = Negative temperature sign bit + * - I = ID, 7-bit + * - T = First BCD digit temperature + * - t = Second BCD digit temperature + * - u = Third BCD digit temperature + * The Checksum seems to covers the 4 data bytes and is something like Fletcher-8. + **/ + +#define TX_8300_PACKAGE_SIZE 32 + +static const SubGhzBlockConst ws_protocol_tx_8300_const = { + .te_short = 1940, + .te_long = 3880, + .te_delta = 250, + .min_count_bit_for_found = 72, +}; + +struct WSProtocolDecoderTX_8300 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + uint32_t package_1; + uint32_t package_2; +}; + +struct WSProtocolEncoderTX_8300 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + TX_8300DecoderStepReset = 0, + TX_8300DecoderStepCheckPreambule, + TX_8300DecoderStepSaveDuration, + TX_8300DecoderStepCheckDuration, +} TX_8300DecoderStep; + +const SubGhzProtocolDecoder ws_protocol_tx_8300_decoder = { + .alloc = ws_protocol_decoder_tx_8300_alloc, + .free = ws_protocol_decoder_tx_8300_free, + + .feed = ws_protocol_decoder_tx_8300_feed, + .reset = ws_protocol_decoder_tx_8300_reset, + + .get_hash_data = ws_protocol_decoder_tx_8300_get_hash_data, + .serialize = ws_protocol_decoder_tx_8300_serialize, + .deserialize = ws_protocol_decoder_tx_8300_deserialize, + .get_string = ws_protocol_decoder_tx_8300_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_tx_8300_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_tx_8300 = { + .name = WS_PROTOCOL_TX_8300_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_tx_8300_decoder, + .encoder = &ws_protocol_tx_8300_encoder, +}; + +void* ws_protocol_decoder_tx_8300_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderTX_8300* instance = malloc(sizeof(WSProtocolDecoderTX_8300)); + instance->base.protocol = &ws_protocol_tx_8300; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_tx_8300_free(void* context) { + furi_assert(context); + WSProtocolDecoderTX_8300* instance = context; + free(instance); +} + +void ws_protocol_decoder_tx_8300_reset(void* context) { + furi_assert(context); + WSProtocolDecoderTX_8300* instance = context; + instance->decoder.parser_step = TX_8300DecoderStepReset; +} + +static bool ws_protocol_tx_8300_check_crc(WSProtocolDecoderTX_8300* instance) { + if(!instance->package_2) return false; + if(instance->package_1 != ~instance->package_2) return false; + + uint16_t x = 0; + uint16_t y = 0; + for(int i = 0; i < 32; i += 4) { + x += (instance->package_1 >> i) & 0x0F; + y += (instance->package_1 >> i) & 0x05; + } + uint8_t crc = (~x & 0xF) << 4 | (~y & 0xF); + return (crc == ((instance->decoder.decode_data) & 0xFF)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_tx_8300_remote_controller(WSBlockGeneric* instance) { + instance->humidity = (((instance->data >> 28) & 0x0F) * 10) + ((instance->data >> 24) & 0x0F); + instance->btn = WS_NO_BTN; + if(!((instance->data >> 22) & 0x03)) + instance->battery_low = 0; + else + instance->battery_low = 1; + instance->channel = (instance->data >> 20) & 0x03; + instance->id = (instance->data >> 12) & 0x7F; + + float temp_raw = ((instance->data >> 8) & 0x0F) * 10.0f + ((instance->data >> 4) & 0x0F) + + (instance->data & 0x0F) * 0.1f; + if(!((instance->data >> 19) & 1)) { + instance->temp = temp_raw; + } else { + instance->temp = -temp_raw; + } +} + +void ws_protocol_decoder_tx_8300_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderTX_8300* instance = context; + + switch(instance->decoder.parser_step) { + case TX_8300DecoderStepReset: + if((level) && (DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_short * 2) < + ws_protocol_tx_8300_const.te_delta)) { + instance->decoder.parser_step = TX_8300DecoderStepCheckPreambule; + } + break; + + case TX_8300DecoderStepCheckPreambule: + if((!level) && ((DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_short * 2) < + ws_protocol_tx_8300_const.te_delta) || + (DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_short * 3) < + ws_protocol_tx_8300_const.te_delta))) { + instance->decoder.parser_step = TX_8300DecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 1; + instance->package_1 = 0; + instance->package_2 = 0; + } else { + instance->decoder.parser_step = TX_8300DecoderStepReset; + } + break; + + case TX_8300DecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = TX_8300DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = TX_8300DecoderStepReset; + } + break; + + case TX_8300DecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)ws_protocol_tx_8300_const.te_short * 5)) { + //Found syncPostfix + if((instance->decoder.decode_count_bit == + ws_protocol_tx_8300_const.min_count_bit_for_found) && + ws_protocol_tx_8300_check_crc(instance)) { + instance->generic.data = instance->package_1; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_tx_8300_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 1; + instance->decoder.parser_step = TX_8300DecoderStepReset; + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_tx_8300_const.te_short) < + ws_protocol_tx_8300_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_long) < + ws_protocol_tx_8300_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = TX_8300DecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_tx_8300_const.te_short) < + ws_protocol_tx_8300_const.te_delta) && + (DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_short) < + ws_protocol_tx_8300_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = TX_8300DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = TX_8300DecoderStepReset; + } + + if(instance->decoder.decode_count_bit == TX_8300_PACKAGE_SIZE) { + instance->package_1 = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } else if(instance->decoder.decode_count_bit == TX_8300_PACKAGE_SIZE * 2) { + instance->package_2 = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + + } else { + instance->decoder.parser_step = TX_8300DecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_tx_8300_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderTX_8300* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_tx_8300_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderTX_8300* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_tx_8300_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderTX_8300* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != ws_protocol_tx_8300_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_tx_8300_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderTX_8300* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/tx_8300.h b/applications/plugins/weather_station/protocols/tx_8300.h new file mode 100644 index 000000000..ec198e80f --- /dev/null +++ b/applications/plugins/weather_station/protocols/tx_8300.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_TX_8300_NAME "TX8300" + +typedef struct WSProtocolDecoderTX_8300 WSProtocolDecoderTX_8300; +typedef struct WSProtocolEncoderTX_8300 WSProtocolEncoderTX_8300; + +extern const SubGhzProtocolDecoder ws_protocol_tx_8300_decoder; +extern const SubGhzProtocolEncoder ws_protocol_tx_8300_encoder; +extern const SubGhzProtocol ws_protocol_tx_8300; + +/** + * Allocate WSProtocolDecoderTX_8300. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderTX_8300* pointer to a WSProtocolDecoderTX_8300 instance + */ +void* ws_protocol_decoder_tx_8300_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderTX_8300. + * @param context Pointer to a WSProtocolDecoderTX_8300 instance + */ +void ws_protocol_decoder_tx_8300_free(void* context); + +/** + * Reset decoder WSProtocolDecoderTX_8300. + * @param context Pointer to a WSProtocolDecoderTX_8300 instance + */ +void ws_protocol_decoder_tx_8300_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderTX_8300 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_tx_8300_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderTX_8300 instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_tx_8300_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderTX_8300. + * @param context Pointer to a WSProtocolDecoderTX_8300 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_tx_8300_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderTX_8300. + * @param context Pointer to a WSProtocolDecoderTX_8300 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_tx_8300_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderTX_8300 instance + * @param output Resulting text + */ +void ws_protocol_decoder_tx_8300_get_string(void* context, FuriString* output); From 1dc79fddf0eaf4b3f774b0189a8e00150f420f44 Mon Sep 17 00:00:00 2001 From: Adam Boeglin Date: Thu, 15 Dec 2022 12:02:43 -0800 Subject: [PATCH 15/21] Added support for IDTECK cards (#2134) --- lib/lfrfid/protocols/lfrfid_protocols.c | 2 + lib/lfrfid/protocols/lfrfid_protocols.h | 1 + lib/lfrfid/protocols/protocol_idteck.c | 269 ++++++++++++++++++++++++ lib/lfrfid/protocols/protocol_idteck.h | 4 + 4 files changed, 276 insertions(+) create mode 100644 lib/lfrfid/protocols/protocol_idteck.c create mode 100644 lib/lfrfid/protocols/protocol_idteck.h diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index bd29bd8e0..2c1f0ad97 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -1,6 +1,7 @@ #include "lfrfid_protocols.h" #include "protocol_em4100.h" #include "protocol_h10301.h" +#include "protocol_idteck.h" #include "protocol_indala26.h" #include "protocol_io_prox_xsf.h" #include "protocol_awid.h" @@ -19,6 +20,7 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, [LFRFIDProtocolH10301] = &protocol_h10301, + [LFRFIDProtocolIdteck] = &protocol_idteck, [LFRFIDProtocolIndala26] = &protocol_indala26, [LFRFIDProtocolIOProxXSF] = &protocol_io_prox_xsf, [LFRFIDProtocolAwid] = &protocol_awid, diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 26065c9aa..848f003a3 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -10,6 +10,7 @@ typedef enum { typedef enum { LFRFIDProtocolEM4100, LFRFIDProtocolH10301, + LFRFIDProtocolIdteck, LFRFIDProtocolIndala26, LFRFIDProtocolIOProxXSF, LFRFIDProtocolAwid, diff --git a/lib/lfrfid/protocols/protocol_idteck.c b/lib/lfrfid/protocols/protocol_idteck.c new file mode 100644 index 000000000..033fcd28c --- /dev/null +++ b/lib/lfrfid/protocols/protocol_idteck.c @@ -0,0 +1,269 @@ +#include +#include +#include +#include "lfrfid_protocols.h" + +// Example: 4944544B 351FBE4B +// 01001001 01000100 01010100 01001011 00110101 00011111 10111110 01001011 +// 4 9 4 4 5 4 4 B 3 5 1 F B E 4 B +// 0100 1001 0100 0100 0101 0100 0100 1011 0011 0101 0001 1111 1011 1110 0100 1011 + +#define IDTECK_PREAMBLE_BIT_SIZE (32) +#define IDTECK_PREAMBLE_DATA_SIZE (8) + +#define IDTECK_ENCODED_BIT_SIZE (64) +#define IDTECK_ENCODED_DATA_SIZE (((IDTECK_ENCODED_BIT_SIZE) / 8) + IDTECK_PREAMBLE_DATA_SIZE) +#define IDTECK_ENCODED_DATA_LAST ((IDTECK_ENCODED_BIT_SIZE) / 8) + +#define IDTECK_DECODED_BIT_SIZE (64) +#define IDTECK_DECODED_DATA_SIZE (8) + +#define IDTECK_US_PER_BIT (255) +#define IDTECK_ENCODER_PULSES_PER_BIT (16) + +typedef struct { + uint8_t data_index; + uint8_t bit_clock_index; + bool last_bit; + bool current_polarity; + bool pulse_phase; +} ProtocolIdteckEncoder; + +typedef struct { + uint8_t encoded_data[IDTECK_ENCODED_DATA_SIZE]; + uint8_t negative_encoded_data[IDTECK_ENCODED_DATA_SIZE]; + uint8_t corrupted_encoded_data[IDTECK_ENCODED_DATA_SIZE]; + uint8_t corrupted_negative_encoded_data[IDTECK_ENCODED_DATA_SIZE]; + + uint8_t data[IDTECK_DECODED_DATA_SIZE]; + ProtocolIdteckEncoder encoder; +} ProtocolIdteck; + +ProtocolIdteck* protocol_idteck_alloc(void) { + ProtocolIdteck* protocol = malloc(sizeof(ProtocolIdteck)); + return protocol; +}; + +void protocol_idteck_free(ProtocolIdteck* protocol) { + free(protocol); +}; + +uint8_t* protocol_idteck_get_data(ProtocolIdteck* protocol) { + return protocol->data; +}; + +void protocol_idteck_decoder_start(ProtocolIdteck* protocol) { + memset(protocol->encoded_data, 0, IDTECK_ENCODED_DATA_SIZE); + memset(protocol->negative_encoded_data, 0, IDTECK_ENCODED_DATA_SIZE); + memset(protocol->corrupted_encoded_data, 0, IDTECK_ENCODED_DATA_SIZE); + memset(protocol->corrupted_negative_encoded_data, 0, IDTECK_ENCODED_DATA_SIZE); +}; + +static bool protocol_idteck_check_preamble(uint8_t* data, size_t bit_index) { + // Preamble 01001001 01000100 01010100 01001011 + if(*(uint32_t*)&data[bit_index / 8] != 0b01001011010101000100010001001001) return false; + return true; +} + +static bool protocol_idteck_can_be_decoded(uint8_t* data) { + if(!protocol_idteck_check_preamble(data, 0)) return false; + return true; +} + +static bool protocol_idteck_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) { + time += (IDTECK_US_PER_BIT / 2); + + size_t bit_count = (time / IDTECK_US_PER_BIT); + bool result = false; + + if(bit_count < IDTECK_ENCODED_BIT_SIZE) { + for(size_t i = 0; i < bit_count; i++) { + bit_lib_push_bit(data, IDTECK_ENCODED_DATA_SIZE, polarity); + if(protocol_idteck_can_be_decoded(data)) { + result = true; + break; + } + } + } + + return result; +} + +static void protocol_idteck_decoder_save(uint8_t* data_to, const uint8_t* data_from) { + bit_lib_copy_bits(data_to, 0, 64, data_from, 0); +} + +bool protocol_idteck_decoder_feed(ProtocolIdteck* protocol, bool level, uint32_t duration) { + bool result = false; + + if(duration > (IDTECK_US_PER_BIT / 2)) { + if(protocol_idteck_decoder_feed_internal(level, duration, protocol->encoded_data)) { + protocol_idteck_decoder_save(protocol->data, protocol->encoded_data); + FURI_LOG_D("Idteck", "Positive"); + result = true; + return result; + } + + if(protocol_idteck_decoder_feed_internal( + !level, duration, protocol->negative_encoded_data)) { + protocol_idteck_decoder_save(protocol->data, protocol->negative_encoded_data); + FURI_LOG_D("Idteck", "Negative"); + result = true; + return result; + } + } + + if(duration > (IDTECK_US_PER_BIT / 4)) { + // Try to decode wrong phase synced data + if(level) { + duration += 120; + } else { + if(duration > 120) { + duration -= 120; + } + } + + if(protocol_idteck_decoder_feed_internal( + level, duration, protocol->corrupted_encoded_data)) { + protocol_idteck_decoder_save(protocol->data, protocol->corrupted_encoded_data); + FURI_LOG_D("Idteck", "Positive Corrupted"); + + result = true; + return result; + } + + if(protocol_idteck_decoder_feed_internal( + !level, duration, protocol->corrupted_negative_encoded_data)) { + protocol_idteck_decoder_save( + protocol->data, protocol->corrupted_negative_encoded_data); + FURI_LOG_D("Idteck", "Negative Corrupted"); + + result = true; + return result; + } + } + + return result; +}; + +bool protocol_idteck_encoder_start(ProtocolIdteck* protocol) { + memset(protocol->encoded_data, 0, IDTECK_ENCODED_DATA_SIZE); + *(uint32_t*)&protocol->encoded_data[0] = 0b01001011010101000100010001001001; + bit_lib_copy_bits(protocol->encoded_data, 32, 32, protocol->data, 32); + + protocol->encoder.last_bit = + bit_lib_get_bit(protocol->encoded_data, IDTECK_ENCODED_BIT_SIZE - 1); + protocol->encoder.data_index = 0; + protocol->encoder.current_polarity = true; + protocol->encoder.pulse_phase = true; + protocol->encoder.bit_clock_index = 0; + + return true; +}; + +LevelDuration protocol_idteck_encoder_yield(ProtocolIdteck* protocol) { + LevelDuration level_duration; + ProtocolIdteckEncoder* encoder = &protocol->encoder; + + if(encoder->pulse_phase) { + level_duration = level_duration_make(encoder->current_polarity, 1); + encoder->pulse_phase = false; + } else { + level_duration = level_duration_make(!encoder->current_polarity, 1); + encoder->pulse_phase = true; + + encoder->bit_clock_index++; + if(encoder->bit_clock_index >= IDTECK_ENCODER_PULSES_PER_BIT) { + encoder->bit_clock_index = 0; + + bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index); + + if(current_bit != encoder->last_bit) { + encoder->current_polarity = !encoder->current_polarity; + } + + encoder->last_bit = current_bit; + + bit_lib_increment_index(encoder->data_index, IDTECK_ENCODED_BIT_SIZE); + } + } + + return level_duration; +}; + +// factory code +static uint32_t get_fc(const uint8_t* data) { + uint32_t fc = 0; + fc = bit_lib_get_bits_32(data, 0, 32); + return fc; +} + +// card number +static uint32_t get_card(const uint8_t* data) { + uint32_t cn = 0; + cn = bit_lib_get_bits_32(data, 32, 32); + return cn; +} + +void protocol_idteck_render_data_internal(ProtocolIdteck* protocol, FuriString* result, bool brief) { + const uint32_t fc = get_fc(protocol->data); + const uint32_t card = get_card(protocol->data); + + if(brief) { + furi_string_printf(result, "FC: %08lX\r\nCard: %08lX", fc, card); + } else { + furi_string_printf( + result, + "FC: %08lX\r\n" + "Card: %08lX\r\n", + fc, + card); + } +} +void protocol_idteck_render_data(ProtocolIdteck* protocol, FuriString* result) { + protocol_idteck_render_data_internal(protocol, result, false); +} +void protocol_idteck_render_brief_data(ProtocolIdteck* protocol, FuriString* result) { + protocol_idteck_render_data_internal(protocol, result, true); +} + +bool protocol_idteck_write_data(ProtocolIdteck* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_idteck_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_BITRATE_RF_32 | LFRFID_T5577_MODULATION_PSK1 | + (2 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.blocks_to_write = 3; + result = true; + } + return result; +}; + +const ProtocolBase protocol_idteck = { + .name = "Idteck", + .manufacturer = "IDTECK", + .data_size = IDTECK_DECODED_DATA_SIZE, + .features = LFRFIDFeaturePSK, + .validate_count = 6, + .alloc = (ProtocolAlloc)protocol_idteck_alloc, + .free = (ProtocolFree)protocol_idteck_free, + .get_data = (ProtocolGetData)protocol_idteck_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_idteck_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_idteck_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_idteck_encoder_start, + .yield = (ProtocolEncoderYield)protocol_idteck_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_idteck_render_data, + .render_brief_data = (ProtocolRenderData)protocol_idteck_render_brief_data, + .write_data = (ProtocolWriteData)protocol_idteck_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_idteck.h b/lib/lfrfid/protocols/protocol_idteck.h new file mode 100644 index 000000000..b7a5ade47 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_idteck.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_idteck; From b5e7bb3334d7dd61c3c0e0bc2d7c2d36bf4deeaa Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Fri, 16 Dec 2022 00:25:43 +0400 Subject: [PATCH 16/21] [FL-3043] SubGhz: add SMC5326, UNILARM protocol (#2138) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SubGhz: add SMC5326 protocol * SubGhz: add unit_test smc5326 protocol Co-authored-by: あく --- .../debug/unit_tests/subghz/subghz_test.c | 17 +- assets/unit_tests/subghz/smc5326.sub | 8 + assets/unit_tests/subghz/smc5326_raw.sub | 7 + assets/unit_tests/subghz/test_random_raw.sub | 4 +- lib/subghz/protocols/protocol_items.c | 2 +- lib/subghz/protocols/protocol_items.h | 1 + lib/subghz/protocols/smc5326.c | 387 ++++++++++++++++++ lib/subghz/protocols/smc5326.h | 107 +++++ 8 files changed, 530 insertions(+), 3 deletions(-) create mode 100644 assets/unit_tests/subghz/smc5326.sub create mode 100644 assets/unit_tests/subghz/smc5326_raw.sub create mode 100644 lib/subghz/protocols/smc5326.c create mode 100644 lib/subghz/protocols/smc5326.h diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index fe834c606..d954ddd94 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -13,7 +13,7 @@ #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 244 +#define TEST_RANDOM_COUNT_PARSE 253 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -587,6 +587,13 @@ MU_TEST(subghz_decoder_ansonic_test) { "Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n"); } +MU_TEST(subghz_decoder_smc5326_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/smc5326_raw.sub"), SUBGHZ_PROTOCOL_SMC5326_NAME), + "Test decoder " SUBGHZ_PROTOCOL_SMC5326_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -714,6 +721,12 @@ MU_TEST(subghz_encoder_ansonic_test) { "Test encoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n"); } +MU_TEST(subghz_encoder_smc5326_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/smc5326.sub")), + "Test encoder " SUBGHZ_PROTOCOL_SMC5326_NAME " error\r\n"); +} + MU_TEST(subghz_random_test) { mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); } @@ -757,6 +770,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_intertechno_v3_test); MU_RUN_TEST(subghz_decoder_clemsa_test); MU_RUN_TEST(subghz_decoder_ansonic_test); + MU_RUN_TEST(subghz_decoder_smc5326_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); @@ -779,6 +793,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_intertechno_v3_test); MU_RUN_TEST(subghz_encoder_clemsa_test); MU_RUN_TEST(subghz_encoder_ansonic_test); + MU_RUN_TEST(subghz_encoder_smc5326_test); MU_RUN_TEST(subghz_random_test); subghz_test_deinit(); diff --git a/assets/unit_tests/subghz/smc5326.sub b/assets/unit_tests/subghz/smc5326.sub new file mode 100644 index 000000000..eab7aac99 --- /dev/null +++ b/assets/unit_tests/subghz/smc5326.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: SMC5326 +Bit: 25 +Key: 00 00 00 00 01 7D 55 80 +TE: 210 diff --git a/assets/unit_tests/subghz/smc5326_raw.sub b/assets/unit_tests/subghz/smc5326_raw.sub new file mode 100644 index 000000000..f2e3edbf0 --- /dev/null +++ b/assets/unit_tests/subghz/smc5326_raw.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 2442 -312 275 -972 949 -310 941 -322 923 -342 921 -352 923 -334 281 -954 945 -350 279 -958 907 -354 289 -980 909 -352 281 -962 907 -330 311 -964 913 -350 317 -930 933 -344 921 -352 893 -330 311 -954 943 -318 315 -958 909 -324 947 -7854 953 -322 289 -948 939 -354 927 -332 911 -324 943 -344 917 -318 317 -964 905 -344 303 -942 947 -312 319 -960 913 -348 281 -958 941 -322 295 -978 905 -350 279 -962 931 -328 947 -324 939 -346 267 -964 935 -348 283 -938 953 -318 931 -7868 935 -346 269 -968 953 -310 941 -322 921 -330 935 -342 931 -318 311 -962 939 -290 337 -950 909 -352 317 -924 943 -324 313 -938 941 -318 317 -932 939 -344 301 -938 933 -350 921 -322 959 -310 301 -942 933 -352 317 -926 957 -314 919 -7868 943 -314 317 -958 909 -322 951 -344 919 -352 921 -324 937 -326 281 -964 941 -318 317 -930 939 -344 301 -938 933 -352 281 -962 953 -314 317 -922 933 -330 315 -954 943 -318 921 -342 943 -320 291 -980 909 -354 281 -962 943 -296 967 -7836 943 -332 309 -950 935 -318 929 -340 943 -320 921 -344 921 -354 283 -960 943 -296 309 -964 945 -318 279 -964 941 -322 333 -944 939 -314 279 -992 903 -342 319 -932 933 -330 931 -340 929 -348 281 -964 935 -334 281 -970 927 -346 921 -7862 951 -314 319 -922 953 -320 923 -346 921 -320 965 -298 943 -324 313 -942 941 -320 317 -930 941 -344 303 -940 945 -312 321 -940 953 -314 303 -960 933 -348 287 -962 911 -352 917 -350 905 -324 333 -918 971 -322 317 -924 945 -324 937 -7872 919 -324 317 -942 941 -318 933 -330 943 -324 943 -310 951 -318 317 -930 939 -344 301 -938 933 -352 317 -926 953 -314 319 -924 939 -324 331 -950 907 -354 315 -926 945 -324 939 -312 953 -318 317 -930 937 -344 301 -940 947 -348 909 -7864 949 -310 319 -956 915 -350 919 -348 905 -322 963 -296 935 -348 317 -922 951 -322 295 -976 939 -314 281 -996 915 -326 307 -940 959 -310 301 -966 935 -346 285 -958 915 -348 921 -348 903 -354 303 -948 911 -350 315 -926 945 -324 941 -7874 943 -290 319 -942 973 -318 929 -314 937 -328 941 -324 939 -310 303 -962 933 -352 285 -962 949 -314 319 -924 951 -320 293 -948 941 -354 283 -962 943 -294 309 -966 943 -320 931 -328 943 -326 311 -940 939 -320 309 -958 933 -338 943 -7840 933 -352 277 -964 941 -322 923 -344 923 -350 931 -310 955 -320 291 -974 907 -350 281 -958 963 -298 313 -956 945 -314 311 -960 937 -312 311 -966 909 -324 319 -944 941 -354 929 -298 945 -324 315 -940 +RAW_Data: 943 -354 281 -964 905 -330 933 -7868 951 -324 315 -938 943 -354 893 -330 943 -324 943 -344 919 -318 317 -962 903 -344 301 -974 903 -350 317 -932 931 -342 269 -972 949 -346 285 -938 955 -310 301 -964 935 -348 921 -320 921 -344 301 -940 935 -350 317 -930 929 -318 937 -7872 939 -344 301 -940 947 -346 917 -322 921 -344 923 -352 927 -334 281 -970 925 -334 277 -982 943 -318 317 -932 931 -344 301 -936 935 -350 281 -960 957 -312 303 -960 935 -346 907 -322 929 -344 301 -942 935 -350 317 -924 955 -312 951 -7858 919 -342 309 -940 949 -348 909 -322 923 -344 923 -352 923 -336 317 -924 945 -312 311 -966 921 -340 317 -924 947 -350 281 -958 941 -322 291 -976 905 -350 279 -960 935 -342 943 -320 919 -330 311 -958 943 -320 315 -932 935 -344 919 -7866 957 -312 303 -964 917 -342 945 -320 923 -344 923 -354 929 -298 315 -956 941 -318 315 -960 911 -324 317 -942 939 -354 281 -964 941 -294 311 -968 943 -318 317 -932 937 -330 931 -350 919 -348 283 -960 917 -350 317 -922 939 -322 965 -7864 921 -324 329 -950 909 -354 923 -336 913 -322 947 -344 919 -354 281 -962 941 -294 311 -960 935 -354 281 -962 939 -294 311 -964 937 -354 281 -964 941 -296 309 -964 939 -318 931 -330 945 -324 315 -940 939 -354 281 -964 909 -344 921 -7862 963 -304 307 -976 933 -320 929 -328 941 -324 939 -348 915 -320 317 -930 939 -344 301 -940 965 -320 319 -926 953 -312 303 -960 933 -312 321 -960 913 -348 319 -924 943 -320 959 -310 921 -354 319 -924 943 -324 311 -938 941 -318 957 -7862 943 -318 317 -932 933 -344 925 -352 897 -332 943 -324 943 -346 267 -966 951 -310 321 -960 911 -350 281 -958 949 -320 291 -978 937 -316 279 -964 949 -326 309 -944 943 -314 959 -318 933 -336 317 -934 933 -344 267 -964 937 -350 905 -7896 943 -318 319 -926 955 -314 919 -350 935 -324 941 -294 967 -312 303 -962 933 -348 285 -960 917 -348 317 -922 941 -322 329 -950 907 -354 315 -926 943 -326 313 -940 941 -352 893 -332 949 -324 315 -938 941 -352 283 -962 943 -310 925 -7890 931 -344 269 -968 949 -310 943 -320 923 -350 937 -310 955 -318 317 -930 935 -344 301 -942 947 -346 285 -958 915 -346 317 -924 951 -322 295 -982 905 -352 317 -924 945 -324 941 -346 917 -318 317 -962 905 -330 311 -956 937 -352 897 -7878 939 -354 283 -960 941 -294 965 -312 953 -318 385 -201512 165 -198 265 -526 229 -298 755 -164 61687 -17310 131 -1056 99 -296 195 -296 65 -66 1617 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index a6a3c9866..900b26207 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -165,4 +165,6 @@ RAW_Data: 1125 -536 1145 -19444 567 -542 1151 -1086 581 -534 1133 -1084 583 -530 RAW_Data: 317 -144 57 -486 53 -282 115 -585 97 -72 229 -174 257 -440 225 -86 173 -518 243 -167 95 -259 137 -96 694 -58 227 -80 279 -287 71 -72 301 -72 121 -106 51 -84 57 -58 199 -260 143 -288 219 -174 113 -681 115 -172 403 -58 113 -116 113 -432 171 -202 55 -108 95 -212 113 -72 527 -166 95 -212 195 -108 603 -142 239 -296 173 -346 373 -287 53 -80 79 -72 95 -238 95 -312 167 -618 143 -288 95 -72 95 -72 141 -210 55 -258 143 -328 305 -58 87 -86 315 -116 195 -218 85 -290 285 -220 215 -189 201 -58 57 -645 119 -96 71 -144 119 -406 143 -72 191 -72 631 -268 344 -56 115 -260 315 -140 455 -518 57 -58 171 -144 488 -86 219 -232 257 -144 85 -174 171 -260 115 -56 87 -166 197 -58 83 -56 85 -288 113 -410 115 -172 163 -202 113 -58 201 -144 201 -86 143 -264 167 -212 113 -116 139 -72 181 -287 343 -430 201 -260 201 -462 143 -192 301 -230 191 -454 187 -144 315 -164 143 -477 165 -58 201 -114 143 -490 115 -86 201 -58 113 -88 85 -58 203 -198 375 -86 171 -346 95 -88 257 -170 81 -56 143 -172 335 -230 173 -202 133 -471 187 -264 215 -86 115 -198 159 -72 179 -112 195 -116 449 -216 93 -96 167 -216 71 -216 71 -166 235 -86 447 -102 101 -226 195 -213 71 -144 215 -144 215 -261 241 -136 269 -142 263 -311 215 -172 201 -144 265 -168 71 -404 259 -86 85 -230 115 -650 143 -202 749 -512 248 -316 201 -154 71 -96 95 -360 105 -56 57 -432 95 -288 95 -286 95 -96 166 -144 93 -144 167 -150 904 -162 95 -526 287 -244 95 -240 383 -120 167 -394 430 -854 95 -72 143 -194 227 -120 167 -264 405 -144 143 -72 143 -72 141 -120 187 -86 143 -164 170 -96 143 -58 143 -86 402 -166 153 -120 95 -96 69 -96 71 -359 404 -338 71 -225 93 -74 97 -54 161 -114 319 -288 113 -116 459 -202 115 -114 115 -116 143 -86 57 -56 87 -114 85 -375 113 -58 311 -240 203 -288 95 -72 119 -383 213 -384 115 -86 171 -58 53 -104 401 -58 115 -86 373 -116 143 -144 161 -216 406 -72 263 -96 215 -72 95 -94 167 -96 191 -240 95 -94 214 -120 403 -116 200 -114 57 -172 220 -120 137 -364 334 -392 115 -260 199 -116 373 -188 95 -110 143 -172 87 -114 172 -230 57 -316 201 -56 249 -485 171 -202 87 -86 85 -144 345 -86 171 -58 259 -58 295 -120 95 -120 71 -192 635 -118 167 -96 375 -72 119 -120 261 -144 167 -96 95 -96 923 -215 71 -433 71 -477 RAW_Data: 191 -240 85 -72 637 -408 213 -510 261 -168 143 -126 79 -106 167 -72 117 -218 251 -168 119 -96 215 -182 191 -238 517 -116 201 -144 255 -154 97 -94 215 -72 95 -120 71 -288 261 -106 434 -96 606 -232 229 -432 85 -174 343 -58 329 -156 55 -116 259 -144 488 -56 307 -339 115 -202 334 -88 113 -86 57 -174 143 -144 401 -376 85 -240 267 -82 95 -216 137 -158 85 -144 143 -58 221 -308 295 -114 87 -114 301 -120 358 -517 71 -262 191 -144 57 -140 165 -407 53 -262 217 -120 238 -358 119 -357 71 -72 119 -96 428 -72 95 -72 167 -72 93 -240 335 -96 357 -240 173 -230 143 -114 87 -200 143 -232 287 -150 97 -288 71 -72 93 -288 115 -58 143 -230 109 -264 71 -72 119 -72 238 -242 97 -78 163 -86 115 -518 79 -560 205 -449 969 -144 507 -86 231 -114 345 -58 979 -110 85 -288 287 -404 229 -202 57 -274 233 -86 115 -202 632 -230 85 -312 369 -392 460 -450 75 -280 85 -202 201 -86 229 -174 143 -144 233 -528 115 -212 127 -202 287 -172 403 -172 139 -128 165 -138 261 -392 143 -480 142 -189 291 -80 53 -283 167 -140 113 -1008 191 -144 119 -120 71 -193 241 -462 201 -58 143 -344 539 -316 113 -174 85 -116 113 -250 239 -168 405 -168 239 -158 85 -144 115 -86 57 -86 341 -144 171 -202 85 -202 115 -114 719 -88 55 -318 257 -56 254 -86 171 -116 459 -174 171 -329 95 -134 85 -314 431 -306 77 -316 401 -86 173 -404 281 -1073 488 -94 217 -78 101 -98 214 -120 215 -340 403 -535 143 -564 115 -116 199 -58 85 -174 315 -58 335 -136 55 -260 143 -144 229 -460 143 -58 143 -144 171 -202 115 -374 291 -130 339 -82 143 -58 171 -58 201 -86 85 -174 1022 -56 85 -82 255 -240 103 -202 431 -278 95 -216 119 -72 71 -96 71 -559 57 -144 171 -88 113 -86 231 -414 131 -192 237 -360 95 -168 145 -168 213 -120 167 -96 143 -110 57 -86 259 -56 87 -777 295 -96 57 -86 173 -86 171 -404 143 -172 231 -200 57 -441 55 -58 173 -56 87 -86 171 -72 287 -72 119 -262 119 -144 71 -72 121 -310 71 -302 113 -54 193 -80 307 -58 257 -232 143 -56 143 -116 219 -72 695 -70 71 -460 85 -232 719 -363 57 -402 604 -230 287 -138 83 -172 259 -58 171 -174 55 -88 489 -114 143 -116 171 -116 143 -58 199 -144 145 -343 374 -186 235 -140 77 -86 143 -202 143 -144 113 -144 143 -58 732 -96 263 -264 71 -206 95 -168 215 -144 271 -80 139 -88 85 -414 75 -100 RAW_Data: 285 -96 627 -362 53 -84 201 -374 113 -202 115 -202 421 -316 85 -58 139 -224 87 -86 229 -58 243 -178 267 -288 95 -336 171 -96 213 -288 71 -405 95 -96 95 -384 95 -72 213 -72 95 -96 95 -272 87 -1083 85 -58 113 -88 257 -116 143 -292 175 -318 95 -120 95 -144 95 -72 71 -216 368 -116 373 -172 115 -58 85 -116 143 -86 85 -144 201 -86 201 -202 257 -144 201 -174 113 -144 115 -144 257 -202 585 -364 173 -138 287 -422 431 -86 85 -96 869 -186 95 -52 115 -86 115 -58 55 -276 365 -86 85 -489 171 -140 577 -106 718 -144 391 -232 195 -82 143 -172 109 -120 167 -96 280 -216 145 -240 215 -186 163 -96 141 -172 159 -603 257 -108 629 -192 119 -80 87 -172 57 -144 286 -86 57 -230 344 -58 113 -537 75 -96 537 -86 403 -196 167 -264 119 -238 119 -120 167 -96 95 -478 95 -120 167 -216 1085 -96 358 -72 263 -72 69 -120 143 -96 71 -96 191 -362 55 -144 57 -260 113 -58 85 -174 55 -88 257 -86 231 -194 55 -58 115 -56 55 -339 55 -58 374 -172 139 -82 419 -98 119 -261 71 -72 71 -240 713 -86 143 -218 295 -72 53 -56 431 -58 317 -144 161 -144 373 -144 173 -144 57 -114 85 -116 195 -72 708 -172 115 -86 191 -96 506 -120 71 -174 85 -58 363 -114 317 -230 316 -200 87 -114 57 -230 115 -315 173 -280 694 -212 453 -256 143 -202 113 -540 352 -116 257 -116 457 -56 109 -58 143 -230 259 -144 259 -525 119 -408 247 -112 389 -72 431 -96 137 -236 97 -474 201 -298 71 -82 55 -116 55 -112 199 -174 191 -86 143 -144 115 -114 317 -86 85 -230 87 -114 259 -84 107 -130 143 -94 153 -86 135 -94 215 -72 239 -94 435 -96 263 -142 166 -334 87 -194 179 -96 115 -284 135 -56 57 -144 463 -204 143 -316 201 -58 403 -86 141 -288 85 -202 139 -397 171 -174 305 -202 85 -144 373 -253 161 -492 181 -191 95 -216 315 -191 71 -166 97 -126 337 -96 71 -96 189 -168 295 -84 197 -86 259 -345 137 -144 167 -796 115 -344 455 -72 119 -96 119 -550 209 -88 85 -86 143 -340 167 -260 143 -537 85 -226 51 -537 57 -260 315 -461 51 -84 199 -358 383 -96 143 -257 115 -86 173 -86 201 -144 143 -316 85 -86 479 -88 85 -72 71 -104 115 -116 267 -72 137 -144 143 -116 85 -86 373 -288 115 -200 87 -114 259 -114 259 -462 143 -144 171 -86 57 -58 137 -144 57 -634 343 -72 205 -86 143 -258 57 -232 113 -230 461 -58 185 -74 537 -86 -RAW_Data: 535 -142 57 -58 55 -116 115 -432 85 -172 259 -192 167 -120 117 -72 119 -240 334 -72 71 -267 285 -144 119 -374 85 -88 85 -114 143 -202 229 -58 143 -202 115 -202 171 -86 71 -144 87 -56 173 -373 143 -116 113 -462 169 -80 215 -148 115 -336 85 -230 163 -432 85 -374 639 -174 85 -58 57 -82 295 -352 269 -532 414 -322 95 -287 263 -268 115 -56 259 -76 85 -282 401 -305 516 -114 115 -202 171 -86 451 -110 85 -346 201 -274 149 -202 85 -364 366 -258 57 -114 259 -172 142 -144 85 -116 85 -480 171 -144 57 -352 115 -116 535 -404 315 -202 163 -158 517 -316 215 -98 85 -346 85 -144 87 -86 257 -82 167 -58 85 -116 113 -894 233 -186 77 -266 147 -72 71 -82 57 -86 171 -58 57 -86 201 -364 143 -202 115 -114 85 -88 113 -86 87 -230 57 -76 613 -72 85 -96 209 -346 458 -58 547 -490 201 -315 315 -116 75 -168 359 -335 95 -384 93 -120 71 -312 251 -366 233 -96 189 -240 263 -192 271 -58 115 -58 229 -346 459 -174 113 -144 173 -144 218 -224 57 -116 215 -72 103 -202 513 -210 433 -116 113 -174 650 -273 147 -450 375 -86 115 -172 536 -84 85 -230 85 -58 195 -468 287 -110 551 -214 167 -311 213 -250 85 -58 85 -355 113 -230 115 -144 117 -288 195 -202 57 -376 123 -144 236 -168 553 -284 119 -72 143 -188 161 -120 93 -312 335 -58 55 -260 105 -244 143 -120 381 -268 173 -268 635 -168 453 -318 71 -167 71 -406 191 -172 215 -408 119 -144 93 -120 97 -130 143 -192 308 -122 147 -550 313 -96 139 -162 167 -96 431 -80 83 -112 201 -86 287 -86 229 -116 57 -288 113 -174 143 -116 113 -144 115 -518 57 -230 57 -172 231 -86 113 -314 183 -144 119 -72 165 -446 81 -86 135 -190 143 -96 71 -72 411 -96 143 -120 69 -216 349 -72 95 -96 517 -646 163 -86 113 -116 171 -116 143 -116 113 -287 259 -114 517 -168 141 -116 105 -72 95 -96 311 -118 159 -310 191 -54 143 -258 115 -450 219 -54 339 -372 239 -72 167 -174 113 -58 57 -144 259 -172 143 -336 113 -174 85 -230 83 -668 85 -202 113 -144 57 -116 373 -316 719 -288 115 -58 75 -120 139 -144 229 -144 57 -144 171 -192 391 -202 403 -58 315 -188 259 -56 115 -144 85 -404 57 -58 105 -102 429 -406 81 -172 57 -144 287 -230 287 -220 317 -458 283 -58 113 -86 269 -72 281 -58 85 -202 113 -52 421 -58 229 -480 259 -58 143 -660 155 -638 123 -86 57 -86 143 -346 143 -144 57 -144 \ No newline at end of file +RAW_Data: 535 -142 57 -58 55 -116 115 -432 85 -172 259 -192 167 -120 117 -72 119 -240 334 -72 71 -267 285 -144 119 -374 85 -88 85 -114 143 -202 229 -58 143 -202 115 -202 171 -86 71 -144 87 -56 173 -373 143 -116 113 -462 169 -80 215 -148 115 -336 85 -230 163 -432 85 -374 639 -174 85 -58 57 -82 295 -352 269 -532 414 -322 95 -287 263 -268 115 -56 259 -76 85 -282 401 -305 516 -114 115 -202 171 -86 451 -110 85 -346 201 -274 149 -202 85 -364 366 -258 57 -114 259 -172 142 -144 85 -116 85 -480 171 -144 57 -352 115 -116 535 -404 315 -202 163 -158 517 -316 215 -98 85 -346 85 -144 87 -86 257 -82 167 -58 85 -116 113 -894 233 -186 77 -266 147 -72 71 -82 57 -86 171 -58 57 -86 201 -364 143 -202 115 -114 85 -88 113 -86 87 -230 57 -76 613 -72 85 -96 209 -346 458 -58 547 -490 201 -315 315 -116 75 -168 359 -335 95 -384 93 -120 71 -312 251 -366 233 -96 189 -240 263 -192 271 -58 115 -58 229 -346 459 -174 113 -144 173 -144 218 -224 57 -116 215 -72 103 -202 513 -210 433 -116 113 -174 650 -273 147 -450 375 -86 115 -172 536 -84 85 -230 85 -58 195 -468 287 -110 551 -214 167 -311 213 -250 85 -58 85 -355 113 -230 115 -144 117 -288 195 -202 57 -376 123 -144 236 -168 553 -284 119 -72 143 -188 161 -120 93 -312 335 -58 55 -260 105 -244 143 -120 381 -268 173 -268 635 -168 453 -318 71 -167 71 -406 191 -172 215 -408 119 -144 93 -120 97 -130 143 -192 308 -122 147 -550 313 -96 139 -162 167 -96 431 -80 83 -112 201 -86 287 -86 229 -116 57 -288 113 -174 143 -116 113 -144 115 -518 57 -230 57 -172 231 -86 113 -314 183 -144 119 -72 165 -446 81 -86 135 -190 143 -96 71 -72 411 -96 143 -120 69 -216 349 -72 95 -96 517 -646 163 -86 113 -116 171 -116 143 -116 113 -287 259 -114 517 -168 141 -116 105 -72 95 -96 311 -118 159 -310 191 -54 143 -258 115 -450 219 -54 339 -372 239 -72 167 -174 113 -58 57 -144 259 -172 143 -336 113 -174 85 -230 83 -668 85 -202 113 -144 57 -116 373 -316 719 -288 115 -58 75 -120 139 -144 229 -144 57 -144 171 -192 391 -202 403 -58 315 -188 259 -56 115 -144 85 -404 57 -58 105 -102 429 -406 81 -172 57 -144 287 -230 287 -220 317 -458 283 -58 113 -86 269 -72 281 -58 85 -202 113 -52 421 -58 229 -480 259 -58 143 -660 155 -638 123 -86 57 -86 143 -346 143 -144 57 -144 +RAW_Data: 2442 -312 275 -972 949 -310 941 -322 923 -342 921 -352 923 -334 281 -954 945 -350 279 -958 907 -354 289 -980 909 -352 281 -962 907 -330 311 -964 913 -350 317 -930 933 -344 921 -352 893 -330 311 -954 943 -318 315 -958 909 -324 947 -7854 953 -322 289 -948 939 -354 927 -332 911 -324 943 -344 917 -318 317 -964 905 -344 303 -942 947 -312 319 -960 913 -348 281 -958 941 -322 295 -978 905 -350 279 -962 931 -328 947 -324 939 -346 267 -964 935 -348 283 -938 953 -318 931 -7868 935 -346 269 -968 953 -310 941 -322 921 -330 935 -342 931 -318 311 -962 939 -290 337 -950 909 -352 317 -924 943 -324 313 -938 941 -318 317 -932 939 -344 301 -938 933 -350 921 -322 959 -310 301 -942 933 -352 317 -926 957 -314 919 -7868 943 -314 317 -958 909 -322 951 -344 919 -352 921 -324 937 -326 281 -964 941 -318 317 -930 939 -344 301 -938 933 -352 281 -962 953 -314 317 -922 933 -330 315 -954 943 -318 921 -342 943 -320 291 -980 909 -354 281 -962 943 -296 967 -7836 943 -332 309 -950 935 -318 929 -340 943 -320 921 -344 921 -354 283 -960 943 -296 309 -964 945 -318 279 -964 941 -322 333 -944 939 -314 279 -992 903 -342 319 -932 933 -330 931 -340 929 -348 281 -964 935 -334 281 -970 927 -346 921 -7862 951 -314 319 -922 953 -320 923 -346 921 -320 965 -298 943 -324 313 -942 941 -320 317 -930 941 -344 303 -940 945 -312 321 -940 953 -314 303 -960 933 -348 287 -962 911 -352 917 -350 905 -324 333 -918 971 -322 317 -924 945 -324 937 -7872 919 -324 317 -942 941 -318 933 -330 943 -324 943 -310 951 -318 317 -930 939 -344 301 -938 933 -352 317 -926 953 -314 319 -924 939 -324 331 -950 907 -354 315 -926 945 -324 939 -312 953 -318 317 -930 937 -344 301 -940 947 -348 909 -7864 949 -310 319 -956 915 -350 919 -348 905 -322 963 -296 935 -348 317 -922 951 -322 295 -976 939 -314 281 -996 915 -326 307 -940 959 -310 301 -966 935 -346 285 -958 915 -348 921 -348 903 -354 303 -948 911 -350 315 -926 945 -324 941 -7874 943 -290 319 -942 973 -318 929 -314 937 -328 941 -324 939 -310 303 -962 933 -352 285 -962 949 -314 319 -924 951 -320 293 -948 941 -354 283 -962 943 -294 309 -966 943 -320 931 -328 943 -326 311 -940 939 -320 309 -958 933 -338 943 -7840 933 -352 277 -964 941 -322 923 -344 923 -350 931 -310 955 -320 291 -974 907 -350 281 -958 963 -298 313 -956 945 -314 311 -960 937 -312 311 -966 909 -324 319 -944 941 -354 929 -298 945 -324 315 -940 +RAW_Data: 943 -354 281 -964 905 -330 933 -7868 951 -324 315 -938 943 -354 893 -330 943 -324 943 -344 919 -318 317 -962 903 -344 301 -974 903 -350 317 -932 931 -342 269 -972 949 -346 285 -938 955 -310 301 -964 935 -348 921 -320 921 -344 301 -940 935 -350 317 -930 929 -318 937 -7872 939 -344 301 -940 947 -346 917 -322 921 -344 923 -352 927 -334 281 -970 925 -334 277 -982 943 -318 317 -932 931 -344 301 -936 935 -350 281 -960 957 -312 303 -960 935 -346 907 -322 929 -344 301 -942 935 -350 317 -924 955 -312 951 -7858 919 -342 309 -940 949 -348 909 -322 923 -344 923 -352 923 -336 317 -924 945 -312 311 -966 921 -340 317 -924 947 -350 281 -958 941 -322 291 -976 905 -350 279 -960 935 -342 943 -320 919 -330 311 -958 943 -320 315 -932 935 -344 919 -7866 957 -312 303 -964 917 -342 945 -320 923 -344 923 -354 929 -298 315 -956 941 -318 315 -960 911 -324 317 -942 939 -354 281 -964 941 -294 311 -968 943 -318 317 -932 937 -330 931 -350 919 -348 283 -960 917 -350 317 -922 939 -322 965 -7864 921 -324 329 -950 909 -354 923 -336 913 -322 947 -344 919 -354 281 -962 941 -294 311 -960 935 -354 281 -962 939 -294 311 -964 937 -354 281 -964 941 -296 309 -964 939 -318 931 -330 945 -324 315 -940 939 -354 281 -964 909 -344 921 -7862 963 -304 307 -976 933 -320 929 -328 941 -324 939 -348 915 -320 317 -930 939 -344 301 -940 965 -320 319 -926 953 -312 303 -960 933 -312 321 -960 913 -348 319 -924 943 -320 959 -310 921 -354 319 -924 943 -324 311 -938 941 -318 957 -7862 943 -318 317 -932 933 -344 925 -352 897 -332 943 -324 943 -346 267 -966 951 -310 321 -960 911 -350 281 -958 949 -320 291 -978 937 -316 279 -964 949 -326 309 -944 943 -314 959 -318 933 -336 317 -934 933 -344 267 -964 937 -350 905 -7896 943 -318 319 -926 955 -314 919 -350 935 -324 941 -294 967 -312 303 -962 933 -348 285 -960 917 -348 317 -922 941 -322 329 -950 907 -354 315 -926 943 -326 313 -940 941 -352 893 -332 949 -324 315 -938 941 -352 283 -962 943 -310 925 -7890 931 -344 269 -968 949 -310 943 -320 923 -350 937 -310 955 -318 317 -930 935 -344 301 -942 947 -346 285 -958 915 -346 317 -924 951 -322 295 -982 905 -352 317 -924 945 -324 941 -346 917 -318 317 -962 905 -330 311 -956 937 -352 897 -7878 939 -354 283 -960 941 -294 965 -312 953 -318 385 -201512 165 -198 265 -526 229 -298 755 -164 61687 -17310 131 -1056 99 -296 195 -296 65 -66 1617 diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 24aaae8df..ed46f5c97 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -12,7 +12,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec, &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, &subghz_protocol_honeywell_wdb, &subghz_protocol_magellan, &subghz_protocol_intertechno_v3, - &subghz_protocol_clemsa, &subghz_protocol_ansonic, + &subghz_protocol_clemsa, &subghz_protocol_ansonic, &subghz_protocol_smc5326, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 114dc5046..9f4467394 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -36,5 +36,6 @@ #include "intertechno_v3.h" #include "clemsa.h" #include "ansonic.h" +#include "smc5326.h" extern const SubGhzProtocolRegistry subghz_protocol_registry; diff --git a/lib/subghz/protocols/smc5326.c b/lib/subghz/protocols/smc5326.c new file mode 100644 index 000000000..889e39f05 --- /dev/null +++ b/lib/subghz/protocols/smc5326.c @@ -0,0 +1,387 @@ +#include "smc5326.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://datasheetspdf.com/pdf-file/532079/Aslic/AX5326-4/1 + * + */ + +#define TAG "SubGhzProtocolSMC5326" + +#define DIP_P 0b11 //(+) +#define DIP_O 0b10 //(0) +#define DIP_N 0b00 //(-) + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define SHOW_DIP_P(dip, check_dip) \ + ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_') + +static const SubGhzBlockConst subghz_protocol_smc5326_const = { + .te_short = 300, + .te_long = 900, + .te_delta = 200, + .min_count_bit_for_found = 25, +}; + +struct SubGhzProtocolDecoderSMC5326 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t te; + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderSMC5326 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint32_t te; +}; + +typedef enum { + SMC5326DecoderStepReset = 0, + SMC5326DecoderStepSaveDuration, + SMC5326DecoderStepCheckDuration, +} SMC5326DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_smc5326_decoder = { + .alloc = subghz_protocol_decoder_smc5326_alloc, + .free = subghz_protocol_decoder_smc5326_free, + + .feed = subghz_protocol_decoder_smc5326_feed, + .reset = subghz_protocol_decoder_smc5326_reset, + + .get_hash_data = subghz_protocol_decoder_smc5326_get_hash_data, + .serialize = subghz_protocol_decoder_smc5326_serialize, + .deserialize = subghz_protocol_decoder_smc5326_deserialize, + .get_string = subghz_protocol_decoder_smc5326_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_smc5326_encoder = { + .alloc = subghz_protocol_encoder_smc5326_alloc, + .free = subghz_protocol_encoder_smc5326_free, + + .deserialize = subghz_protocol_encoder_smc5326_deserialize, + .stop = subghz_protocol_encoder_smc5326_stop, + .yield = subghz_protocol_encoder_smc5326_yield, +}; + +const SubGhzProtocol subghz_protocol_smc5326 = { + .name = SUBGHZ_PROTOCOL_SMC5326_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_smc5326_decoder, + .encoder = &subghz_protocol_smc5326_encoder, +}; + +void* subghz_protocol_encoder_smc5326_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSMC5326* instance = malloc(sizeof(SubGhzProtocolEncoderSMC5326)); + + instance->base.protocol = &subghz_protocol_smc5326; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_smc5326_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSMC5326* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderSMC5326 instance + * @return true On success + */ +static bool subghz_protocol_encoder_smc5326_get_upload(SubGhzProtocolEncoderSMC5326* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)instance->te * 3); + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)instance->te * 3); + } + } + + //Send Stop bit + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + //Send PT_GUARD + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 25); + + return true; +} + +bool subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSMC5326* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_smc5326_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_smc5326_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_smc5326_stop(void* context) { + SubGhzProtocolEncoderSMC5326* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_smc5326_yield(void* context) { + SubGhzProtocolEncoderSMC5326* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_smc5326_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSMC5326* instance = malloc(sizeof(SubGhzProtocolDecoderSMC5326)); + instance->base.protocol = &subghz_protocol_smc5326; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_smc5326_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + free(instance); +} + +void subghz_protocol_decoder_smc5326_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + instance->decoder.parser_step = SMC5326DecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + + switch(instance->decoder.parser_step) { + case SMC5326DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_short * 24) < + subghz_protocol_smc5326_const.te_delta * 12)) { + //Found Preambula + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + } + break; + case SMC5326DecoderStepSaveDuration: + //save duration + if(level) { + instance->decoder.te_last = duration; + instance->te += duration; + instance->decoder.parser_step = SMC5326DecoderStepCheckDuration; + } + break; + case SMC5326DecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_smc5326_const.te_long * 2)) { + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + if(instance->decoder.decode_count_bit == + subghz_protocol_smc5326_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->te /= (instance->decoder.decode_count_bit * 4 + 1); + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->last_data = instance->decoder.decode_data; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + break; + } + + instance->te += duration; + + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_smc5326_const.te_short) < + subghz_protocol_smc5326_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_long) < + subghz_protocol_smc5326_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_smc5326_const.te_long) < + subghz_protocol_smc5326_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_short) < + subghz_protocol_smc5326_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = SMC5326DecoderStepReset; + } + } else { + instance->decoder.parser_step = SMC5326DecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_smc5326_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_smc5326_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + res = true; + } while(false); + + return res; +} + +static void subghz_protocol_smc5326_get_event_serialize(uint8_t event, FuriString* output) { + furi_string_cat_printf( + output, + "%s%s%s%s\r\n", + (((event >> 6) & 0x3) == 0x3 ? "B1 " : ""), + (((event >> 4) & 0x3) == 0x3 ? "B2 " : ""), + (((event >> 2) & 0x3) == 0x3 ? "B3 " : ""), + (((event >> 0) & 0x3) == 0x3 ? "B4 " : "")); +} + +void subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + uint32_t data = (uint32_t)((instance->generic.data >> 9) & 0xFFFF); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%07lX Te:%ldus\r\n" + " +: " DIP_PATTERN "\r\n" + " o: " DIP_PATTERN " ", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0x1FFFFFF), + instance->te, + SHOW_DIP_P(data, DIP_P), + SHOW_DIP_P(data, DIP_O)); + subghz_protocol_smc5326_get_event_serialize(instance->generic.data >> 1, output); + furi_string_cat_printf(output, " -: " DIP_PATTERN "\r\n", SHOW_DIP_P(data, DIP_N)); +} diff --git a/lib/subghz/protocols/smc5326.h b/lib/subghz/protocols/smc5326.h new file mode 100644 index 000000000..ddc954bd5 --- /dev/null +++ b/lib/subghz/protocols/smc5326.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_SMC5326_NAME "SMC5326" + +typedef struct SubGhzProtocolDecoderSMC5326 SubGhzProtocolDecoderSMC5326; +typedef struct SubGhzProtocolEncoderSMC5326 SubGhzProtocolEncoderSMC5326; + +extern const SubGhzProtocolDecoder subghz_protocol_smc5326_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_smc5326_encoder; +extern const SubGhzProtocol subghz_protocol_smc5326; + +/** + * Allocate SubGhzProtocolEncoderSMC5326. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSMC5326* pointer to a SubGhzProtocolEncoderSMC5326 instance + */ +void* subghz_protocol_encoder_smc5326_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSMC5326. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + */ +void subghz_protocol_encoder_smc5326_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + */ +void subghz_protocol_encoder_smc5326_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_smc5326_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderSMC5326. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderSMC5326* pointer to a SubGhzProtocolDecoderSMC5326 instance + */ +void* subghz_protocol_decoder_smc5326_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + */ +void subghz_protocol_decoder_smc5326_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + */ +void subghz_protocol_decoder_smc5326_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_smc5326_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* output); From 3681a5478c864ffd1cae664af7c05e62631c9a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Fri, 16 Dec 2022 08:04:15 +0900 Subject: [PATCH 17/21] [FL-3044] Dolphin: add new animation --- .../L1_Happy_holidays_128x64/frame_0.png | Bin 0 -> 1787 bytes .../L1_Happy_holidays_128x64/frame_1.png | Bin 0 -> 1795 bytes .../L1_Happy_holidays_128x64/frame_10.png | Bin 0 -> 1791 bytes .../L1_Happy_holidays_128x64/frame_11.png | Bin 0 -> 1814 bytes .../L1_Happy_holidays_128x64/frame_12.png | Bin 0 -> 1779 bytes .../L1_Happy_holidays_128x64/frame_2.png | Bin 0 -> 1774 bytes .../L1_Happy_holidays_128x64/frame_3.png | Bin 0 -> 1801 bytes .../L1_Happy_holidays_128x64/frame_4.png | Bin 0 -> 1799 bytes .../L1_Happy_holidays_128x64/frame_5.png | Bin 0 -> 1797 bytes .../L1_Happy_holidays_128x64/frame_6.png | Bin 0 -> 1789 bytes .../L1_Happy_holidays_128x64/frame_7.png | Bin 0 -> 1804 bytes .../L1_Happy_holidays_128x64/frame_8.png | Bin 0 -> 1809 bytes .../L1_Happy_holidays_128x64/frame_9.png | Bin 0 -> 1816 bytes .../L1_Happy_holidays_128x64/meta.txt | 23 ++++++++++++++++++ assets/dolphin/external/manifest.txt | 13 +++++++--- 15 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png create mode 100644 assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..909cc330392b999e38b0326b5381344c952380b8 GIT binary patch literal 1787 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png new file mode 100644 index 0000000000000000000000000000000000000000..3a43c6b844541f9be3cadc2aa78864e9147919c6 GIT binary patch literal 1791 zcmVnvyf3eBg-+&c%%cGpOsw&ZJ*b5*$z;l?5Yi0jy7XFZ9XH9ar)E7M=0e0a+Ci( z*H%Q2(@-5?W+Nw@07V4tJf0gn{`g#T0?YV)Y^APuj|?se z%wo3_XlwL;BVyKdU19uBz)K3K-u#Yqz>+ZSnT!yhp3gOSd--#KNP{F|$U6{2D@90T zb^8>D(e!+21hHjLCR{RkI08$a|KbQkCoyWq)1?u(OR=_A+r`sG&SQhH~mDq%3O>Yy?=Ilw#UdV2kEcJ z|1^18l+nhqE#sYidTXyypvp&LSs93c8Z^vU`HW33+N~ewYdpf5TLtFEvk_5(A@*Ax zU_8V~)uEMY?0V=K(+ynN%V6bMPFsO;2k?kg#tA&e?aA;C%St~hk#!~+rDh$W+ySiH zYE=)O6KKYX8jLwCuVs9<&k3p=A@pfrl$6Nv5u(+J%or`?YfqjRDY7JM7L}<)b~B?$ zeHJ9wP4(ivowkk6{ul2ybEWQm(Or>;k2p2`8(5al&q zytyY}m73^%IlsCRkUB)(mtfk{BVHna)kHx+<38a7k_;lwi!K9dUyt)MH=B)0bQLg` zaoT%*(X1jTfeaL#!yxl{N{01^8bNILE7L8zqZ4GKlSUrLTWVDvRdxGJ`8Vp=Uq41+ z+5(<12}`l46L=jtZ;n?9>~Kir5S5NFBkN%u#F%%ydYkQ4drt6)oWaZQ5>Y`OJ`Mn=e{w+ykX)}gf)|3=W?M5VVMTKHz1U>5SSNYaeXh&e;lruO8> z=c8_E2VfQV=CjZ?V9BvI>!mD|v$e6Bj;LCaGFU`pF-}nC02N@!COiK0SjGV~c?eAU z_2y_}`?HsC1@C|t$}BD+390pgk2TifDM+q2R@(#8Ym~#nBj6sa>Z#8^@`Vt2h+em> zk74AW9y_9HUr2srUG40*{y+pGji|xl9g;V;-(1PMQD{YLL=Yu+Rbchq{tF>qpP)7F zv*HjVa-%#ZvsTp}Wo1JBT>#tYVaG8F&eXP++>RA;vz=2-hQ>WsaWt0$j7p)- zpj3WFX|{gW=2>9s=#1P5G$+u~*NmeIijt zavZC>sNqDDO@q{vasVcj&xCt#E;7Enxd>zAis&|j*;JSXb7rlzc0C8pR@J1IBHEhr zJx%FM5b(1ifW2JSLpy=7s*me8ViMIt;}IDYy9Zk$huWcpK9WX%1ZTX z;c|jB{%oZ1Zi6I56$Z4{V-MT96fALLQ`GKM@#X-Uf!W&~!OGHyG4Kq5Wudj7EnQBL z#3KfsGTv0Ro-b4V~63n$)d(UL0@Fx)i~~L9UKs%_C951OJANj` zX^kRFmgYPwdM)05-u^BCEk2_NLItn51ES8XMTtoH>|x{~*7-iP2?)ru**u4{!{KzqK5I3i?oWoFz;UKB+%t#*AMY#DSS0<6A? zOam&Sd!zwX|23?V1N7HYc9otUqD~w!tn95s)`_fzWdBtGmREX)l8cPv zR&oHOr}r$P6JqKXSi{JQDiRr@%`R)s%MV0U8mb~cqEgc13Dq9FFv!TPESbD9cQJg&%8T&wSwN;2(4m!TUKxmF hpSXqJX8hJ#`vpJ+v^_(~quc-h002ovPDHLkV1n0aQh)#e literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png new file mode 100644 index 0000000000000000000000000000000000000000..20ea12eb06d4e6d10d9dcea9f1d2c69277765e1a GIT binary patch literal 1814 zcmV+x2kH2UP)F$kQo{QqB`eacqE2AWGUy4zKj8gn6mK!ct= z=W!g@@y~Ew_Lk4{+$72Uc-$?(Zd}*(uN3UzJpPdfm~Z}ho~9?*X(XOU>cYI|vA-uX zkQMNbn`J50Q?l&C^tWWN#%Jg50#g2QI0jG^c;$wXu~n3Z#xwIVMt`^REwplgPu=7y zba;=up&CGTGtvd`fVPfC_%vnxRdT4g|KlJzKos-A@31*0d|A~mV}`;=^?&7o+EgB`89yZh9k=Sgo9)aF>9pX zVleO`9HJ1!=FQPf%Ujj~8Sk~W(Jt3UGO_z&Gk~gbFh=PWv@`;E90?Mbjg@0u=WRhe zBSP?yK`?DQ5VzC%6O}}z4_2Qch0!k)!!a6GFXOe zfsuWy0n7?O!Z8&+?*P0o!;nO2oU1qhJ9pG*BltCdt_dx7DsMA;yCRHx%)27n_=F0f zG}5X}?HoWOAzJppq@Ck=eyz1>pR;R7*}9FX3mL+P5Hf)1VisAOf!gKaqt{w~n@Lr7 zD!DrquJDw{m?VPeVx4IpyPkaZvWai0=4r?p!5az8RKi$f*(?i`DOgo7OFm|;IbUB@ z@yoJ)b{xzCsz$(A<4%v@fhi`ff~T;kLK(6=)}jkftY>kaOPr6BaaYSS1qE@Bs-~kLObpTj=yRPeE z+_<+ntu$-(`72vk>H2Y-U5> zsALN)$@7X=-7>KAzx^U;Oy~{-xpoA9EgV=i2n~_5tAvFp8=etV>JGd6A_HJc?Y5u zMkFDX&t6{{gPD1~zGx4~qs)&?K_}8~0eQ1qeh?xL(IZl$4>SMWeP?JjkuhgSK}l=( zLe#)k4`9VctH9wMl-GAI|7tOvkv#$?<=t75){y#Da0Omv@i@4zkLNOU=OJe1^*Dis zNLZ-sQS-Lfb#Wnpy*1g}HNzrMvY%Yjn-|Gx^<(DuhiBGgFn<&(4?`m`=;n7C0Yc@FDz`C<$a(Bc#VHb~ zvGxk7Vt|aGa_cS3u|G0zl!JT%@tZ;akti$wt=x2Wtd`cWeIxUFDd|{St1d>@-Ff4s zoKHpoEACf~fN_n86v~6IDxK-ON>Sb6Ml)(;tWVh~GO285Zxiw)=nyoav$rmb&f<+W z_exlDKP_cw?GlmCxYK05mJ_c7qX8SDlwxV}){Lri3!5yP^KW5X5-r*Odc9?tbU(=y2s0 zBB|u+eS6u;n*mrJz^Vjh1)JAEuhIMH{7BwPDD%iN!&6og%Q63~FHM#Qa#IMM9AFkw zYmdfQ2}VX)++s0v6HPwvhF6Tw3L{#!q-Fu5qsQF4J-{sF>S`i`4x>h4)5?0}xNJSf z0@B+o(?d2`i=)>as488%$-7xM%AT~xal9(t^Q@JNXN9DAHZYBGB5X1Dya~Z-=lcSuA?DySWym8!P?~1d2T=P8;!m}nhrs@GKcm`v&q4C~2S{{wS?p0<~k#mZiCzS?vD+n1x zWe(ZqrJ+_^cApXBVO%r%obd*ox^g{07noZku|k$;_{g~Cxj$10^u~6b0}7wZJOEle z^4SXFXQi!3o{lrAdK>v#o0I8-u*CyZvw)T8;0fKxcqxYeAiVvNsEn&-1Vm$5QiaB3 zz^wCW`TT0Ud>7zt-1;0<4ZSqbnmg;#D!5bmcU4M%0Cc+*>zlWu!2kdN07*qoM6N<$ Ef-Tf;h5!Hn literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png new file mode 100644 index 0000000000000000000000000000000000000000..a05abd80f1bbcf8c4c31985df242787246776d05 GIT binary patch literal 1779 zcmV0r&z%C3U6&)&Q402Ruv+OXzmGsctVBk~ZZKW%&qr8NNGen&b$i!kXm9wELxpG)xe@^*j-K@u_K6AD8uMQ~(w z`xb|h^t=&4Xxozsmq;FtK$FL706JcC#tfH$b^uxD@SG8qsp)wmf+7lLbEQ-hMA!D@ zY587rTFD_iW3Pnfyxv#|sJ^Kpu*zty>9-JB3oSGy<0ChHSGvkv22WR1_n+Ol%d5z( zjr87cUy?j2%1Gl-%6M1jGP_Pz#*udeJtyE}sllcBk`o{|nvRnk&C_ibD^g2T-ZCCQ z0^M+=>yTPCbUo`egbiH9WoQvkL#9qcQ0@R;A(e3gui~C9h@!@rMaGe=E=rPzo^81U zXt!1C9y~=Q<3tUHL=TTLu5ylS6tP;^?Af*!bbx3W=HsnG3-xfRde`K6UBYfssxpO0 zyhjG9A~3qBR8*}55+QaJc{?!ML3}vC3Obn1S?LV)FzELctsGBv-kx@mNl}O?09Do_ zUc9*{prs7!IiLSTLuPSvfK1Edktp99S}jDJz@yHq+Q2MD4;O(_QAM9=N`8YAX!=EJ zi0qotf_k8WIqaQ$C{#17#=4>7>0(bp_5h{g(Q3ss8JRcJBt0?&(Jkd&*L6wk7+)Yt zz#uH!Z-I420}>Hd0Vy)0Z+i9K+e0+HN<4a%%e0<<8%{ela;BCIO&-AWY|cg@ z2K+`NTX;fxn>o|)CxYG+CqvrlL#sw44Xi>m4cv;rC}#6n^yYXQ^2YMqhsgED@^RK# zaCS0!*(9Lczh$ebH-oBLk;obr&FhV|YL$#!1e(Dk9fS2O`^M6HzwLht*p-{nTPAfO z<9B|mk+GT%mRxTv)!AETC#;%{N3kOx2;quYksuN>pD>ord1ArK_1gN8Fe?{nH=qBk z$+gCgc6NMuW4$#_0!=^YxUCVOHH;@fl(%wr1u}Uac~(_7MdeUED0>P0T>wh-pyMn^ zxxE5b1z#Z-rN(Dc12tR~+!UY{Sk|~%<4J`bI!^;n0abFNb7+)9qg`t`$3dc`nF9D> zX=I0%w^#54*&K~Jl~s#Gp`Agdtz1=u^vjTp^v&AyF=mz((+C{8uGRKP@Wy(qUepOI zoGF5cLqSEL70%EFrMa0_4AE=0fb-JaSle?>KDMGpiU6s5AO}EhYejKmJ?G#Dk9vJ1 z@1k-M@RYVX>`Q4!bat8oR<%?dmS^&3(hbW*9!V}b-%CMUR}IniGC(hrngX+$EBgJg}b$+bY+FHgTLP7zJw$HJ}fQPH1!1>`tv-hF8EqlmDK)|y<;mGf8O z!vQoekAyk|)eM;?YefMY1&tu9jc#2~>GdNKfF3?V&P3O%$hB2YPK(qi`7^t=_4@!+ z1JI(WDgs!!|5RS3Iw3kBtf)74W%x(-@bRHgWK*l0fWD6CWXVMJo7s=l&1ATN6X{^! zXAB(QwOmzQ15hQc-z#KB##il)UYICL>dB3y5pRv3Arx6y`nduptKdp#T!k0qP=oSf z($fJ+Gar8&=o!Oe(@-bLKJ#>{0xHkrG*Ds)Bo}K zv5Udv$mlwtkxSC$sxZDb|HT(T3o|;;>Kd%YpNiEEWf@c|AZIRu=RE2sg190cKOnun zqtfNbtc4nU6%8z*au1ecywEo2LBG^ zCriHd9ChMIAzHr>)FmU?^ z5tW9L9AZWWf6Z${t=DE#64KrwYe+3MGPlZBy>~wXRKfG`N<+=)p^cB|fjVo?8It25(No$3 zV$Y9%5|s(n9=uzC$gIkF$e1S(SCs{)h1+`p87^ReGgn?Gz&Ez?7NU5H@dtK9z<;d< Vp()s7p}_zE002ovPDHLkV1hccKn4H+ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png new file mode 100644 index 0000000000000000000000000000000000000000..fc7e0364afbfa0c0750a59c1d546dc9211c42161 GIT binary patch literal 1774 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png new file mode 100644 index 0000000000000000000000000000000000000000..bcc7f28a9eb40eda4f9d472409a93fab7f0d98ef GIT binary patch literal 1799 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png new file mode 100644 index 0000000000000000000000000000000000000000..c0b1a4fe24af639bddda8ca7f69d2bee8aed8689 GIT binary patch literal 1789 zcmVgl@z3GiqHRtsLHW&}w z$K$%L*Y(Trdf_EM9*<3yoX5{;19s!}di^Q~M|fPnCh;4e0%<>@HZ&zpfwvW({7sU@O>WoM*ky$YX;3wDS>KIlx^P zxe6WG<22j=s4hmQ;2rSvc!bY$*8fTlz4U)wBnQYsZv2ju;RW66^)iFNbN-myY46=L zgBbuF2Ja36OV9Wk5wl_I3g>?Vmi7U5Tn8)(v&M9W`1bqULbR5D0gzd6#+i56NH!3w zLHaELV;+P{HGIlE|m$~vI$dv9!vYu%Pj?7G+rU?w=&fQ%Yi9s#_r0tu|%mE*n6 z(_-|tR@u7@`cz`i(JB91`l0F&M==j*xADc%8jKr(wg=1We)+PtI; zM9`;pjA?kO+fDt!$)p!L!f1Q)dEumY`;ANHu~yl9cI^&0Gn)QFWr%r9@?J zC&8TQ16Y1|N?{!ad4^?!05MwT#G?KHl2Nhk1enGcgJ;x!WfgR!q&sqk2$hn!yAOc6 zuaQpJ2#TVtauUAL$q0A2-ezq0_=%t)FnrOdwNr|3kVZQZ^&OCt*mgMj9*gza)A4o{ zUc~^=3M;6VHCk`9SmYceH=1kd^qdvd{A>_tRLXJ~0J~Fk6a+JUhda#G?{yv+ritXu zA9J?<51jNN+9Y{liRgMpRJj{HD~-bA!9nR@rc+&hw5 zZ$GWQ-Jzd&Jy}{l*tuvBq*@@<{b5Wlpsz{Iy#B(x0dl?3RjB-SRydL?i z!EC^F&DHCTGfPvME2;f+U8mE9C6pS|-l&YvY{hyY7lES{vtyKL&h2MI*wi~d|i z^u8u6GtHeb-y%R6=nQ1pCJkX)m*}v;;zOA}g7t5;4aYKo)`Q68Z2IfIgR^AW%Md$YU4PQX zAAJfVw%> z_YZpL3GZG=2hoW>-vOsfKeL3ruJ~sHbqREG5lDYEOR?8m<~h>8q6x%wZ6(H>k1GHX zFnt|Fcb`6u*0thYKa3s^m5t4E0FR?Ea>hHiX#~j=BiVj*@=-6myL4QMQ`ZL52qNYlB>4-2RpLIZ{Qet5>g0gF=l+D8@Pp?L`0z!}JlkSaI>)>d^XAwDU z#^x*{#*S=^o}*_UB(MWI16T%1!?gAO?GHq(Dod)e&AJs_3+z~RogL*{HI?hRUA_u4 zKo^)(qp`x4?DestX1Cy-((bIOqnfmwyPBhEjejXrNIC}1-DpUp{*(>it f52u`evP=30f`%HTZ&_~;00000NkvXXu0mjfZU|{H literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png new file mode 100644 index 0000000000000000000000000000000000000000..19c8181d0369a8edf52ef2124d403f5472c8e280 GIT binary patch literal 1804 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png new file mode 100644 index 0000000000000000000000000000000000000000..929a207ef58a63e946d60e4413e5ad96a75d7c0a GIT binary patch literal 1816 zcmV+z2j}>SP)fcfU1=V^L^okrq$q%O>R9{YPT z16cv@xLKA`JtfONOn*xTYkYR@E+FL}hhqR$fmd!A8CykpXgo6?WAt|`-$E+~_|#3V zLWlRb8>#_hHzQr}4ruFWgilk}UnPf{`#%nn14J<&{EnNU8QpbV&H>04*8V+c=`X^PI)Dc$9Ae?JuZ*{v+nW!~bLWhkUH$1HBZFng z78u#L8o;aoBpg%G^A5lZGYm*!ARZFPr$5YMzFy5xkMWOeKs(md&z2nSxaXv*csen)CHl z6~8R&XUD-TplSq+HSY8X9++a%DtHQuDwHA1GagJ41ufw9Lw!F1l>$o!)@5KSep#~D zUI>!u+i07AXT8DQuoQ&eR}H&DQX?{i3PBo4id4xeAz?SNO4O>99Xx%CDJnrPb%#ID>}>*x=e)Jj(o&DdH)0SXcaiTgYx>$o*Ya@V0pjMUO7(-t^v$KS(QPp zMxKGRQct#dT(z&hR2(4Ca?Y51-BVb%3B8Bqct4e43u2NKYxY3Ln8S7JaicBio+1rFX2|5H#X!h251rZjV z#T#wzm9XSKujz@EetE2z^#b~_J?aurViZW`j3P2# zo3rvPwU5f!q|riW{SC zJQHK1huA>~E%`hn%$}haMzm~6ljGMPhoXa*M6BClwc=6~Rbtr9Ydiy!=`JJxID7U| zNIB5Bhb5$39KG&9mqBF|s*TAOwd8y2Qq5k%^Q@JNXBp#LA&5zrG>c`8B>Rj(CPY~- z907&PsB*N-$}5OfA@}tb0xSY1!+{mU<#R2pXyJ!hN5c+g9d*SsEQQYAAWFJ3MXFZ_ zkVb&gG^;*<+E=DptFYR5SX2xwg4c&ZQkl{vc@;v`-kmyn$gaa0roCFh>c@7GttJVkicB*#=efCbNBtTr^>TZgUiY3pip&s-~VPLcDZ(!g#7 zA%m#QA=|t()N0G_Gh#e!CD7tkU7^W>>jApJ+!~1$vP8p2#x>9VnM$BHw(A^F_*~`z z(BhG2D~O+!wjy~t&ZO#XSL4@b0p7-~=csDvrGeJmS(jG9oyxzfQu+f7>@m9|qN_##0000 Date: Sat, 17 Dec 2022 02:20:10 +0400 Subject: [PATCH 18/21] [FL-3040] Audio support for SubGhz (#2131) * Furi_hal_speaker: multiple resource usage * Furi_hal_speaker: fix multiple resource usage * Furi_hal_speaker: fix music_player_worker * Furi_hal_speaker: fix mutex release queue handling * SubGhz: add furi_hal_subghz_set_debug_pin * SubGhz: add sound SubGhz Read, SubGhz Read RAW * furi_hal_speaker: add __attribute__((warn_unused_result)) for furi_hal_speaker_acquire() * Furi_hal_speaker: fix review comments * SubGhz: cleanup naming and locking timings * SubGhz,FuriHal: fix speaker deinit logic and subghz speaker release sequence * FuriHal: crash on speaker acquire/release from IRQ * Furi, FuriHal: FURI_WARN_UNUSED and documentation update * Bump api symbols version: fix broken speaker Co-authored-by: Aleksandr Kutuzov --- .../main/subghz/helpers/subghz_types.h | 7 + .../subghz/scenes/subghz_scene_read_raw.c | 3 + .../scenes/subghz_scene_receiver_config.c | 29 ++++ applications/main/subghz/subghz.c | 1 + applications/main/subghz/subghz_i.c | 42 ++++++ applications/main/subghz/subghz_i.h | 5 + .../music_player/music_player_worker.c | 76 ++++++----- .../services/notification/notification_app.c | 9 +- firmware/targets/f7/api_symbols.csv | 7 +- .../targets/f7/furi_hal/furi_hal_speaker.c | 51 ++++++- .../targets/f7/furi_hal/furi_hal_subghz.c | 125 +++++++++--------- .../furi_hal_include/furi_hal_speaker.h | 47 +++++++ .../furi_hal_include/furi_hal_subghz.h | 32 +++-- furi/core/common_defines.h | 4 + 14 files changed, 325 insertions(+), 113 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index bd6451f24..abf705393 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -28,6 +28,13 @@ typedef enum { SubGhzHopperStateRSSITimeOut, } SubGhzHopperState; +/** SubGhzSpeakerState state */ +typedef enum { + SubGhzSpeakerStateDisable, + SubGhzSpeakerStateShutdown, + SubGhzSpeakerStateEnable, +} SubGhzSpeakerState; + /** SubGhzRxKeyState state */ typedef enum { SubGhzRxKeyStateIDLE, diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 2e5ba0966..b270dd482 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -259,6 +259,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { case SubGhzCustomEventViewReadRAWSendStop: subghz->state_notifications = SubGhzNotificationStateIDLE; if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_speaker_unmute(subghz); subghz_tx_stop(subghz); subghz_sleep(subghz); } @@ -376,10 +377,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, false); subghz_protocol_raw_save_to_file_pause( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, true); + subghz_speaker_mute(subghz); } else { subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, rssi, true); subghz_protocol_raw_save_to_file_pause( (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, false); + subghz_speaker_unmute(subghz); } } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index fd42829b5..b49aac922 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -5,6 +5,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, SubGhzSettingIndexHopping, SubGhzSettingIndexModulation, + SubGhzSettingIndexSound, SubGhzSettingIndexLock, SubGhzSettingIndexRAWThesholdRSSI, }; @@ -48,6 +49,16 @@ const uint32_t hopping_value[HOPPING_COUNT] = { SubGhzHopperStateRunnig, }; +#define SPEAKER_COUNT 2 +const char* const speaker_text[SPEAKER_COUNT] = { + "OFF", + "ON", +}; +const uint32_t speaker_value[SPEAKER_COUNT] = { + SubGhzSpeakerStateShutdown, + SubGhzSpeakerStateEnable, +}; + uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { furi_assert(context); SubGhz* subghz = context; @@ -167,6 +178,14 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) subghz->txrx->hopper_state = hopping_value[index]; } +static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, speaker_text[index]); + subghz->txrx->speaker_state = speaker_value[index]; +} + static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -235,6 +254,16 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_text( item, subghz_setting_get_preset_name(subghz->setting, value_index)); + item = variable_item_list_add( + subghz->variable_item_list, + "Sound:", + SPEAKER_COUNT, + subghz_scene_receiver_config_set_speaker, + subghz); + value_index = value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, speaker_text[value_index]); + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != SubGhzCustomEventManagerSet) { variable_item_list_add(subghz->variable_item_list, "Lock Keyboard", 1, NULL, NULL); diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index df5a76525..b7564ab57 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -177,6 +177,7 @@ SubGhz* subghz_alloc() { subghz->txrx->txrx_state = SubGhzTxRxStateSleep; subghz->txrx->hopper_state = SubGhzHopperStateOFF; + subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; subghz->txrx->raw_threshold_rssi = SUBGHZ_RAW_TRESHOLD_MIN; subghz->txrx->history = subghz_history_alloc(); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index 736bcf360..0bcd70061 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -86,6 +86,7 @@ uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) { uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_flush_rx(); + subghz_speaker_on(subghz); furi_hal_subghz_rx(); furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, subghz->txrx->worker); @@ -104,6 +105,7 @@ static bool subghz_tx(SubGhz* subghz, uint32_t frequency) { furi_hal_subghz_set_frequency_and_path(frequency); furi_hal_gpio_write(&gpio_cc1101_g0, false); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + subghz_speaker_on(subghz); bool ret = furi_hal_subghz_tx(); subghz->txrx->txrx_state = SubGhzTxRxStateTx; return ret; @@ -119,11 +121,13 @@ void subghz_idle(SubGhz* subghz) { void subghz_rx_end(SubGhz* subghz) { furi_assert(subghz); furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateRx); + if(subghz_worker_is_running(subghz->txrx->worker)) { subghz_worker_stop(subghz->txrx->worker); furi_hal_subghz_stop_async_rx(); } furi_hal_subghz_idle(); + subghz_speaker_off(subghz); subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; } @@ -212,6 +216,7 @@ void subghz_tx_stop(SubGhz* subghz) { subghz, subghz->txrx->fff_data, furi_string_get_cstr(subghz->file_path)); } subghz_idle(subghz); + subghz_speaker_off(subghz); notification_message(subghz->notifications, &sequence_reset_red); } @@ -585,3 +590,40 @@ void subghz_hopper_update(SubGhz* subghz) { subghz_rx(subghz, subghz->txrx->preset->frequency); } } + +void subghz_speaker_on(SubGhz* subghz) { + if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { + if(furi_hal_speaker_acquire(30)) { + furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + } else { + subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; + } + } +} + +void subghz_speaker_off(SubGhz* subghz) { + if(subghz->txrx->speaker_state != SubGhzSpeakerStateDisable) { + if(furi_hal_speaker_is_mine()) { + furi_hal_subghz_set_async_mirror_pin(NULL); + furi_hal_speaker_release(); + if(subghz->txrx->speaker_state == SubGhzSpeakerStateShutdown) + subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; + } + } +} + +void subghz_speaker_mute(SubGhz* subghz) { + if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { + if(furi_hal_speaker_is_mine()) { + furi_hal_subghz_set_async_mirror_pin(NULL); + } + } +} + +void subghz_speaker_unmute(SubGhz* subghz) { + if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { + if(furi_hal_speaker_is_mine()) { + furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + } + } +} diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 234365155..cd33da447 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -53,6 +53,7 @@ struct SubGhzTxRx { uint16_t idx_menu_chosen; SubGhzTxRxState txrx_state; SubGhzHopperState hopper_state; + SubGhzSpeakerState speaker_state; uint8_t hopper_timeout; uint8_t hopper_idx_frequency; SubGhzRxKeyState rx_key_state; @@ -131,3 +132,7 @@ void subghz_file_name_clear(SubGhz* subghz); bool subghz_path_is_file(FuriString* path); uint32_t subghz_random_serial(void); void subghz_hopper_update(SubGhz* subghz); +void subghz_speaker_on(SubGhz* subghz); +void subghz_speaker_off(SubGhz* subghz); +void subghz_speaker_mute(SubGhz* subghz); +void subghz_speaker_unmute(SubGhz* subghz); diff --git a/applications/plugins/music_player/music_player_worker.c b/applications/plugins/music_player/music_player_worker.c index 0d683f4a6..60fd33a17 100644 --- a/applications/plugins/music_player/music_player_worker.c +++ b/applications/plugins/music_player/music_player_worker.c @@ -47,47 +47,51 @@ static int32_t music_player_worker_thread_callback(void* 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); - 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; - 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--; + 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 *= 0.9945679; + furi_hal_speaker_set_volume(volume); + furi_delay_ms(2); + } + NoteBlockArray_next(it); } - 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 *= 0.9945679; - furi_hal_speaker_set_volume(volume); - furi_delay_ms(2); - } - NoteBlockArray_next(it); } - } - furi_hal_speaker_stop(); + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } else { + FURI_LOG_E(TAG, "Speaker system is busy with another process."); + } return 0; } diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index 6091f0aa7..4f6d42aa0 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -150,11 +150,16 @@ void notification_vibro_off() { } void notification_sound_on(float freq, float volume) { - furi_hal_speaker_start(freq, volume); + if(furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(freq, volume); + } } void notification_sound_off() { - furi_hal_speaker_stop(); + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } } // display timer diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 80aaf7fa6..b3afb8427 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,10.1,, +Version,+,11.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1282,7 +1282,11 @@ Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* +Function,+,furi_hal_speaker_acquire,_Bool,uint32_t +Function,-,furi_hal_speaker_deinit,void, Function,-,furi_hal_speaker_init,void, +Function,+,furi_hal_speaker_is_mine,_Bool, +Function,+,furi_hal_speaker_release,void, Function,+,furi_hal_speaker_set_volume,void,float Function,+,furi_hal_speaker_start,void,"float, float" Function,+,furi_hal_speaker_stop,void, @@ -1316,6 +1320,7 @@ Function,+,furi_hal_subghz_read_packet,void,"uint8_t*, uint8_t*" Function,+,furi_hal_subghz_reset,void, Function,+,furi_hal_subghz_rx,void, Function,+,furi_hal_subghz_rx_pipe_not_empty,_Bool, +Function,+,furi_hal_subghz_set_async_mirror_pin,void,const GpioPin* Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath diff --git a/firmware/targets/f7/furi_hal/furi_hal_speaker.c b/firmware/targets/f7/furi_hal/furi_hal_speaker.c index 03a7f094b..c4a0bdd1e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_speaker.c +++ b/firmware/targets/f7/furi_hal/furi_hal_speaker.c @@ -1,23 +1,66 @@ #include #include #include +#include #include +#include + +#define TAG "FuriHalSpeaker" #define FURI_HAL_SPEAKER_TIMER TIM16 #define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 #define FURI_HAL_SPEAKER_PRESCALER 500 #define FURI_HAL_SPEAKER_MAX_VOLUME 60 +static FuriMutex* furi_hal_speaker_mutex = NULL; + // #define FURI_HAL_SPEAKER_NEW_VOLUME void furi_hal_speaker_init() { + furi_assert(furi_hal_speaker_mutex == NULL); + furi_hal_speaker_mutex = furi_mutex_alloc(FuriMutexTypeNormal); FURI_CRITICAL_ENTER(); LL_TIM_DeInit(FURI_HAL_SPEAKER_TIMER); FURI_CRITICAL_EXIT(); + FURI_LOG_I(TAG, "Init OK"); +} - furi_hal_gpio_init_ex( - &gpio_speaker, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); +void furi_hal_speaker_deinit() { + furi_check(furi_hal_speaker_mutex != NULL); + LL_TIM_DeInit(FURI_HAL_SPEAKER_TIMER); + furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_mutex_free(furi_hal_speaker_mutex); + furi_hal_speaker_mutex = NULL; +} + +bool furi_hal_speaker_acquire(uint32_t timeout) { + furi_check(!FURI_IS_IRQ_MODE()); + + if(furi_mutex_acquire(furi_hal_speaker_mutex, timeout) == FuriStatusOk) { + furi_hal_power_insomnia_enter(); + furi_hal_gpio_init_ex( + &gpio_speaker, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); + return true; + } else { + return false; + } +} + +void furi_hal_speaker_release() { + furi_check(!FURI_IS_IRQ_MODE()); + furi_check(furi_hal_speaker_is_mine()); + + furi_hal_speaker_stop(); + furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_power_insomnia_exit(); + + furi_check(furi_mutex_release(furi_hal_speaker_mutex) == FuriStatusOk); +} + +bool furi_hal_speaker_is_mine() { + return (FURI_IS_IRQ_MODE()) || + (furi_mutex_get_owner(furi_hal_speaker_mutex) == furi_thread_get_current_id()); } static inline uint32_t furi_hal_speaker_calculate_autoreload(float frequency) { @@ -54,6 +97,8 @@ static inline uint32_t furi_hal_speaker_calculate_compare(float volume) { } void furi_hal_speaker_start(float frequency, float volume) { + furi_check(furi_hal_speaker_is_mine()); + if(volume <= 0) { furi_hal_speaker_stop(); return; @@ -75,6 +120,7 @@ void furi_hal_speaker_start(float frequency, float volume) { } void furi_hal_speaker_set_volume(float volume) { + furi_check(furi_hal_speaker_is_mine()); if(volume <= 0) { furi_hal_speaker_stop(); return; @@ -88,6 +134,7 @@ void furi_hal_speaker_set_volume(float volume) { } void furi_hal_speaker_stop() { + furi_check(furi_hal_speaker_is_mine()); LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 726b2d7fa..3441fd965 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -17,39 +16,26 @@ #define TAG "FuriHalSubGhz" -/* - * Uncomment define to enable duplication of - * IO GO0 CC1101 to an external comb. - * Debug pin can be assigned - * gpio_ext_pc0 - * gpio_ext_pc1 - * gpio_ext_pc3 - * gpio_ext_pb2 - * gpio_ext_pb3 - * gpio_ext_pa4 - * gpio_ext_pa6 - * gpio_ext_pa7 - * Attention this setting switches pin to output. - * Make sure it is not connected directly to power or ground - */ - -//#define SUBGHZ_DEBUG_CC1101_PIN gpio_ext_pa7 -#ifdef SUBGHZ_DEBUG_CC1101_PIN -uint32_t subghz_debug_gpio_buff[2]; -#endif +static uint32_t furi_hal_subghz_debug_gpio_buff[2]; typedef struct { volatile SubGhzState state; volatile SubGhzRegulation regulation; volatile FuriHalSubGhzPreset preset; + const GpioPin* async_mirror_pin; } FuriHalSubGhz; volatile FuriHalSubGhz furi_hal_subghz = { .state = SubGhzStateInit, .regulation = SubGhzRegulationTxRx, .preset = FuriHalSubGhzPresetIDLE, + .async_mirror_pin = NULL, }; +void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin) { + furi_hal_subghz.async_mirror_pin = pin; +} + void furi_hal_subghz_init() { furi_assert(furi_hal_subghz.state == SubGhzStateInit); furi_hal_subghz.state = SubGhzStateIdle; @@ -372,6 +358,29 @@ void furi_hal_subghz_set_path(FuriHalSubGhzPath path) { furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } +static bool furi_hal_subghz_start_debug() { + bool ret = false; + if(furi_hal_subghz.async_mirror_pin != NULL) { + furi_hal_gpio_init( + furi_hal_subghz.async_mirror_pin, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh); + ret = true; + } + return ret; +} + +static bool furi_hal_subghz_stop_debug() { + bool ret = false; + if(furi_hal_subghz.async_mirror_pin != NULL) { + furi_hal_gpio_init( + furi_hal_subghz.async_mirror_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + ret = true; + } + return ret; +} + volatile uint32_t furi_hal_subghz_capture_delta_duration = 0; volatile FuriHalSubGhzCaptureCallback furi_hal_subghz_capture_callback = NULL; volatile void* furi_hal_subghz_capture_callback_context = NULL; @@ -382,9 +391,9 @@ static void furi_hal_subghz_capture_ISR() { LL_TIM_ClearFlag_CC1(TIM2); furi_hal_subghz_capture_delta_duration = LL_TIM_IC_GetCaptureCH1(TIM2); if(furi_hal_subghz_capture_callback) { -#ifdef SUBGHZ_DEBUG_CC1101_PIN - furi_hal_gpio_write(&SUBGHZ_DEBUG_CC1101_PIN, false); -#endif + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); + furi_hal_subghz_capture_callback( true, furi_hal_subghz_capture_delta_duration, @@ -395,9 +404,9 @@ static void furi_hal_subghz_capture_ISR() { if(LL_TIM_IsActiveFlag_CC2(TIM2)) { LL_TIM_ClearFlag_CC2(TIM2); if(furi_hal_subghz_capture_callback) { -#ifdef SUBGHZ_DEBUG_CC1101_PIN - furi_hal_gpio_write(&SUBGHZ_DEBUG_CC1101_PIN, true); -#endif + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, true); + furi_hal_subghz_capture_callback( false, LL_TIM_IC_GetCaptureCH2(TIM2) - furi_hal_subghz_capture_delta_duration, @@ -459,10 +468,8 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); -#ifdef SUBGHZ_DEBUG_CC1101_PIN - furi_hal_gpio_init( - &SUBGHZ_DEBUG_CC1101_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -#endif + // Start debug + furi_hal_subghz_start_debug(); // Switch to RX furi_hal_subghz_rx(); @@ -478,9 +485,8 @@ void furi_hal_subghz_stop_async_rx() { FURI_CRITICAL_ENTER(); LL_TIM_DeInit(TIM2); -#ifdef SUBGHZ_DEBUG_CC1101_PIN - furi_hal_gpio_init(&SUBGHZ_DEBUG_CC1101_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -#endif + // Stop debug + furi_hal_subghz_stop_debug(); FURI_CRITICAL_EXIT(); furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); @@ -673,30 +679,27 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); -#ifdef SUBGHZ_DEBUG_CC1101_PIN - furi_hal_gpio_init( - &SUBGHZ_DEBUG_CC1101_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + // Start debug + if(furi_hal_subghz_start_debug()) { + const GpioPin* gpio = furi_hal_subghz.async_mirror_pin; + furi_hal_subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; + furi_hal_subghz_debug_gpio_buff[1] = gpio->pin; - const GpioPin* gpio = &SUBGHZ_DEBUG_CC1101_PIN; - subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; - subghz_debug_gpio_buff[1] = gpio->pin; - - dma_config.MemoryOrM2MDstAddress = (uint32_t)subghz_debug_gpio_buff; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->BSRR); - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = 2; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - -#endif + dma_config.MemoryOrM2MDstAddress = (uint32_t)furi_hal_subghz_debug_gpio_buff; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->BSRR); + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = 2; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); + LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 2); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + } return true; } @@ -730,10 +733,10 @@ void furi_hal_subghz_stop_async_tx() { // Deinitialize GPIO furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -#ifdef SUBGHZ_DEBUG_CC1101_PIN - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - furi_hal_gpio_init(&SUBGHZ_DEBUG_CC1101_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -#endif + // Stop debug + if(furi_hal_subghz_stop_debug()) { + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + } FURI_CRITICAL_EXIT(); diff --git a/firmware/targets/furi_hal_include/furi_hal_speaker.h b/firmware/targets/furi_hal_include/furi_hal_speaker.h index 67de41d92..0b33d9232 100644 --- a/firmware/targets/furi_hal_include/furi_hal_speaker.h +++ b/firmware/targets/furi_hal_include/furi_hal_speaker.h @@ -4,16 +4,63 @@ */ #pragma once +#include + #ifdef __cplusplus extern "C" { #endif +/** Init speaker */ void furi_hal_speaker_init(); +/** Deinit speaker */ +void furi_hal_speaker_deinit(); + +/** Acquire speaker ownership + * + * @warning You must acquire speaker ownership before use + * + * @param timeout Timeout during which speaker ownership must be acquired + * + * @return bool returns true on success + */ +FURI_WARN_UNUSED bool furi_hal_speaker_acquire(uint32_t timeout); + +/** Release speaker ownership + * + * @warning You must release speaker ownership after use + */ +void furi_hal_speaker_release(); + +/** Check current process speaker ownership + * + * @warning always returns true if called from ISR + * + * @return bool returns true if process owns speaker + */ +bool furi_hal_speaker_is_mine(); + +/** Play a note + * + * @warning no ownership check if called from ISR + * + * @param frequency The frequency + * @param volume The volume + */ void furi_hal_speaker_start(float frequency, float volume); +/** Set volume + * + * @warning no ownership check if called from ISR + * + * @param volume The volume + */ void furi_hal_speaker_set_volume(float volume); +/** Stop playback + * + * @warning no ownership check if called from ISR + */ void furi_hal_speaker_stop(); #ifdef __cplusplus diff --git a/firmware/targets/furi_hal_include/furi_hal_subghz.h b/firmware/targets/furi_hal_include/furi_hal_subghz.h index 1f99386c0..102981dbe 100644 --- a/firmware/targets/furi_hal_include/furi_hal_subghz.h +++ b/firmware/targets/furi_hal_include/furi_hal_subghz.h @@ -9,6 +9,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -34,9 +35,9 @@ typedef enum { /** Switchable Radio Paths */ typedef enum { FuriHalSubGhzPathIsolate, /**< Isolate Radio from antenna */ - FuriHalSubGhzPath433, /**< Center Frquency: 433MHz. Path 1: SW1RF1-SW2RF2, LCLCL */ - FuriHalSubGhzPath315, /**< Center Frquency: 315MHz. Path 2: SW1RF2-SW2RF1, LCLCLCL */ - FuriHalSubGhzPath868, /**< Center Frquency: 868MHz. Path 3: SW1RF3-SW2RF3, LCLC */ + FuriHalSubGhzPath433, /**< Center Frequency: 433MHz. Path 1: SW1RF1-SW2RF2, LCLCL */ + FuriHalSubGhzPath315, /**< Center Frequency: 315MHz. Path 2: SW1RF2-SW2RF1, LCLCLCL */ + FuriHalSubGhzPath868, /**< Center Frequency: 868MHz. Path 3: SW1RF3-SW2RF3, LCLC */ } FuriHalSubGhzPath; /** SubGhz state */ @@ -60,8 +61,17 @@ typedef enum { SubGhzRegulationTxRx, /**TxRx*/ } SubGhzRegulation; +/* Mirror RX/TX async modulation signal to specified pin + * + * @warning Configures pin to output mode. Make sure it is not connected + * directly to power or ground. + * + * @param[in] pin pointer to the gpio pin structure or NULL to disable + */ +void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin); + /** Initialize and switch to power save mode Used by internal API-HAL - * initalization routine Can be used to reinitialize device to safe state and + * initialization routine Can be used to reinitialize device to safe state and * send it to sleep */ void furi_hal_subghz_init(); @@ -105,13 +115,13 @@ void furi_hal_subghz_load_patable(const uint8_t data[8]); */ void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size); -/** Check if recieve pipe is not empty +/** Check if receive pipe is not empty * * @return true if not empty */ bool furi_hal_subghz_rx_pipe_not_empty(); -/** Check if recieved data crc is valid +/** Check if received data crc is valid * * @return true if valid */ @@ -132,7 +142,7 @@ void furi_hal_subghz_flush_rx(); */ void furi_hal_subghz_flush_tx(); -/** Shutdown Issue spwd command +/** Shutdown Issue SPWD command * @warning registers content will be lost */ void furi_hal_subghz_shutdown(); @@ -146,7 +156,7 @@ void furi_hal_subghz_reset(); */ void furi_hal_subghz_idle(); -/** Switch to Recieve +/** Switch to Receive */ void furi_hal_subghz_rx(); @@ -172,7 +182,7 @@ uint8_t furi_hal_subghz_get_lqi(); * * @param value frequency in Hz * - * @return true if frequncy is valid, otherwise false + * @return true if frequency is valid, otherwise false */ bool furi_hal_subghz_is_frequency_valid(uint32_t value); @@ -181,7 +191,7 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value); * * @param value frequency in Hz * - * @return real frequency in herz + * @return real frequency in Hz */ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); @@ -189,7 +199,7 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); * * @param value frequency in Hz * - * @return real frequency in herz + * @return real frequency in Hz */ uint32_t furi_hal_subghz_set_frequency(uint32_t value); diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index 31be7fff0..c7acf95b4 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -11,6 +11,10 @@ extern "C" { #include +#ifndef FURI_WARN_UNUSED +#define FURI_WARN_UNUSED __attribute__((warn_unused_result)) +#endif + #ifndef FURI_IS_IRQ_MASKED #define FURI_IS_IRQ_MASKED() (__get_PRIMASK() != 0U) #endif From f10e82c64dc8f42ce2ee66c92aefdaf574abeea8 Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Mon, 19 Dec 2022 12:38:20 +0300 Subject: [PATCH 19/21] [FL-3024] Locale settings (#2137) * Locale settings * Time/date format fix * Locale: add docs, enums for HAL, cleanup. Co-authored-by: Aleksandr Kutuzov --- .../debug/locale_test/application.fam | 11 ++ applications/debug/locale_test/locale_test.c | 102 +++++++++++++ applications/services/locale/application.fam | 9 ++ applications/services/locale/locale.c | 109 +++++++++++++ applications/services/locale/locale.h | 107 +++++++++++++ applications/settings/system/application.fam | 2 +- .../settings/system/system_settings.c | 79 ++++++++++ firmware/targets/f7/api_symbols.csv | 19 ++- firmware/targets/f7/furi_hal/furi_hal_rtc.c | 74 +++++++-- .../targets/furi_hal_include/furi_hal_rtc.h | 143 +++++++++++++++++- 10 files changed, 636 insertions(+), 19 deletions(-) create mode 100644 applications/debug/locale_test/application.fam create mode 100644 applications/debug/locale_test/locale_test.c create mode 100644 applications/services/locale/application.fam create mode 100644 applications/services/locale/locale.c create mode 100644 applications/services/locale/locale.h diff --git a/applications/debug/locale_test/application.fam b/applications/debug/locale_test/application.fam new file mode 100644 index 000000000..e46eeff51 --- /dev/null +++ b/applications/debug/locale_test/application.fam @@ -0,0 +1,11 @@ +App( + appid="locale_test", + name="Locale Test", + apptype=FlipperAppType.DEBUG, + entry_point="locale_test_app", + cdefines=["APP_LOCALE"], + requires=["gui", "locale"], + stack_size=2 * 1024, + order=70, + fap_category="Debug", +) diff --git a/applications/debug/locale_test/locale_test.c b/applications/debug/locale_test/locale_test.c new file mode 100644 index 000000000..003df55dc --- /dev/null +++ b/applications/debug/locale_test/locale_test.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + View* view; +} LocaleTestApp; + +static void locale_test_view_draw_callback(Canvas* canvas, void* _model) { + UNUSED(_model); + + // Prepare canvas + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + + FuriString* tmp_string = furi_string_alloc(); + + float temp = 25.3f; + LocaleMeasurementUnits units = locale_get_measurement_unit(); + if(units == LocaleMeasurementUnitsMetric) { + furi_string_printf(tmp_string, "Temp: %5.1fC", (double)temp); + } else { + temp = locale_celsius_to_fahrenheit(temp); + furi_string_printf(tmp_string, "Temp: %5.1fF", (double)temp); + } + canvas_draw_str(canvas, 0, 10, furi_string_get_cstr(tmp_string)); + + FuriHalRtcDateTime datetime; + furi_hal_rtc_get_datetime(&datetime); + + locale_format_time(tmp_string, &datetime, locale_get_time_format(), false); + canvas_draw_str(canvas, 0, 25, furi_string_get_cstr(tmp_string)); + + locale_format_date(tmp_string, &datetime, locale_get_date_format(), "/"); + canvas_draw_str(canvas, 0, 40, furi_string_get_cstr(tmp_string)); + + furi_string_free(tmp_string); +} + +static bool locale_test_view_input_callback(InputEvent* event, void* context) { + UNUSED(event); + UNUSED(context); + return false; +} + +static uint32_t locale_test_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +static LocaleTestApp* locale_test_alloc() { + LocaleTestApp* app = malloc(sizeof(LocaleTestApp)); + + // 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); + + // Views + app->view = view_alloc(); + view_set_draw_callback(app->view, locale_test_view_draw_callback); + view_set_input_callback(app->view, locale_test_view_input_callback); + + view_set_previous_callback(app->view, locale_test_exit); + view_dispatcher_add_view(app->view_dispatcher, 0, app->view); + view_dispatcher_switch_to_view(app->view_dispatcher, 0); + + return app; +} + +static void locale_test_free(LocaleTestApp* app) { + furi_assert(app); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, 0); + + view_free(app->view); + view_dispatcher_free(app->view_dispatcher); + + // Close gui record + furi_record_close(RECORD_GUI); + app->gui = NULL; + + // Free rest + free(app); +} + +int32_t locale_test_app(void* p) { + UNUSED(p); + LocaleTestApp* app = locale_test_alloc(); + view_dispatcher_run(app->view_dispatcher); + locale_test_free(app); + return 0; +} diff --git a/applications/services/locale/application.fam b/applications/services/locale/application.fam new file mode 100644 index 000000000..c762d02d6 --- /dev/null +++ b/applications/services/locale/application.fam @@ -0,0 +1,9 @@ +App( + appid="locale", + name="LocaleSrv", + apptype=FlipperAppType.STARTUP, + entry_point="locale_on_system_start", + cdefines=["SRV_LOCALE"], + order=90, + sdk_headers=["locale.h"], +) diff --git a/applications/services/locale/locale.c b/applications/services/locale/locale.c new file mode 100644 index 000000000..07f56b1b0 --- /dev/null +++ b/applications/services/locale/locale.c @@ -0,0 +1,109 @@ +#include "locale.h" + +#define TAG "LocaleSrv" + +LocaleMeasurementUnits locale_get_measurement_unit(void) { + return (LocaleMeasurementUnits)furi_hal_rtc_get_locale_units(); +} + +void locale_set_measurement_unit(LocaleMeasurementUnits format) { + furi_hal_rtc_set_locale_units((FuriHalRtcLocaleUnits)format); +} + +LocaleTimeFormat locale_get_time_format(void) { + return (LocaleTimeFormat)furi_hal_rtc_get_locale_timeformat(); +} + +void locale_set_time_format(LocaleTimeFormat format) { + furi_hal_rtc_set_locale_timeformat((FuriHalRtcLocaleTimeFormat)format); +} + +LocaleDateFormat locale_get_date_format(void) { + return (LocaleDateFormat)furi_hal_rtc_get_locale_dateformat(); +} + +void locale_set_date_format(LocaleDateFormat format) { + furi_hal_rtc_set_locale_dateformat((FuriHalRtcLocaleDateFormat)format); +} + +float locale_fahrenheit_to_celsius(float temp_f) { + return (temp_f - 32.f) / 1.8f; +} + +float locale_celsius_to_fahrenheit(float temp_c) { + return (temp_c * 1.8f + 32.f); +} + +void locale_format_time( + FuriString* out_str, + const FuriHalRtcDateTime* datetime, + const LocaleTimeFormat format, + const bool show_seconds) { + furi_assert(out_str); + furi_assert(datetime); + + uint8_t hours = datetime->hour; + uint8_t am_pm = 0; + if(format == LocaleTimeFormat12h) { + if(hours > 12) { + hours -= 12; + am_pm = 2; + } else { + am_pm = 1; + } + } + + if(show_seconds) { + furi_string_printf(out_str, "%02u:%02u:%02u", hours, datetime->minute, datetime->second); + } else { + furi_string_printf(out_str, "%02u:%02u", hours, datetime->minute); + } + + if(am_pm > 0) { + furi_string_cat_printf(out_str, " %s", (am_pm == 1) ? ("AM") : ("PM")); + } +} + +void locale_format_date( + FuriString* out_str, + const FuriHalRtcDateTime* datetime, + const LocaleDateFormat format, + const char* separator) { + furi_assert(out_str); + furi_assert(datetime); + furi_assert(separator); + + if(format == LocaleDateFormatDMY) { + furi_string_printf( + out_str, + "%02u%s%02u%s%04u", + datetime->day, + separator, + datetime->month, + separator, + datetime->year); + } else if(format == LocaleDateFormatMDY) { + furi_string_printf( + out_str, + "%02u%s%02u%s%04u", + datetime->month, + separator, + datetime->day, + separator, + datetime->year); + } else { + furi_string_printf( + out_str, + "%04u%s%02u%s%02u", + datetime->year, + separator, + datetime->month, + separator, + datetime->day); + } +} + +int32_t locale_on_system_start(void* p) { + UNUSED(p); + return 0; +} diff --git a/applications/services/locale/locale.h b/applications/services/locale/locale.h new file mode 100644 index 000000000..5f2a4d87f --- /dev/null +++ b/applications/services/locale/locale.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + LocaleMeasurementUnitsMetric = 0, /**< Metric measurement units */ + LocaleMeasurementUnitsImperial = 1, /**< Imperial measurement units */ +} LocaleMeasurementUnits; + +typedef enum { + LocaleTimeFormat24h = 0, /**< 24-hour format */ + LocaleTimeFormat12h = 1, /**< 12-hour format */ +} LocaleTimeFormat; + +typedef enum { + LocaleDateFormatDMY = 0, /**< Day/Month/Year */ + LocaleDateFormatMDY = 1, /**< Month/Day/Year */ + LocaleDateFormatYMD = 2, /**< Year/Month/Day */ +} LocaleDateFormat; + +/** Get Locale measurement units + * + * @return The locale measurement units. + */ +LocaleMeasurementUnits locale_get_measurement_unit(); + +/** Set locale measurement units + * + * @param[in] format The locale measurements units + */ +void locale_set_measurement_unit(LocaleMeasurementUnits format); + +/** Convert Fahrenheit to Celsius + * + * @param[in] temp_f The Temperature in Fahrenheit + * + * @return The Temperature in Celsius + */ +float locale_fahrenheit_to_celsius(float temp_f); + +/** Convert Celsius to Fahrenheit + * + * @param[in] temp_c The Temperature in Celsius + * + * @return The Temperature in Fahrenheit + */ +float locale_celsius_to_fahrenheit(float temp_c); + +/** Get Locale time format + * + * @return The locale time format. + */ +LocaleTimeFormat locale_get_time_format(); + +/** Set Locale Time Format + * + * @param[in] format The Locale Time Format + */ +void locale_set_time_format(LocaleTimeFormat format); + +/** Format time to furi string + * + * @param[out] out_str The FuriString to store formatted time + * @param[in] datetime Pointer to the datetime + * @param[in] format The Locale Time Format + * @param[in] show_seconds The show seconds flag + */ +void locale_format_time( + FuriString* out_str, + const FuriHalRtcDateTime* datetime, + const LocaleTimeFormat format, + const bool show_seconds); + +/** Get Locale DateFormat + * + * @return The Locale DateFormat. + */ +LocaleDateFormat locale_get_date_format(); + +/** Set Locale DateFormat + * + * @param[in] format The Locale DateFormat + */ +void locale_set_date_format(LocaleDateFormat format); + +/** Format date to furi string + * + * @param[out] out_str The FuriString to store formatted date + * @param[in] datetime Pointer to the datetime + * @param[in] format The format + * @param[in] separator The separator + */ +void locale_format_date( + FuriString* out_str, + const FuriHalRtcDateTime* datetime, + const LocaleDateFormat format, + const char* separator); + +#ifdef __cplusplus +} +#endif diff --git a/applications/settings/system/application.fam b/applications/settings/system/application.fam index 0fc456b2f..69a8f1239 100644 --- a/applications/settings/system/application.fam +++ b/applications/settings/system/application.fam @@ -3,7 +3,7 @@ App( name="System", apptype=FlipperAppType.SETTINGS, entry_point="system_settings_app", - requires=["gui"], + requires=["gui", "locale"], stack_size=1 * 1024, order=70, ) diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c index dfce11a22..5eade2115 100644 --- a/applications/settings/system/system_settings.c +++ b/applications/settings/system/system_settings.c @@ -1,6 +1,7 @@ #include "system_settings.h" #include #include +#include const char* const log_level_text[] = { "Default", @@ -70,6 +71,59 @@ static void heap_trace_mode_changed(VariableItem* item) { furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]); } +const char* const mesurement_units_text[] = { + "Metric", + "Imperial", +}; + +const uint32_t mesurement_units_value[] = { + LocaleMeasurementUnitsMetric, + LocaleMeasurementUnitsImperial, +}; + +static void mesurement_units_changed(VariableItem* item) { + // SystemSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, mesurement_units_text[index]); + locale_set_measurement_unit(mesurement_units_value[index]); +} + +const char* const time_format_text[] = { + "24h", + "12h", +}; + +const uint32_t time_format_value[] = { + LocaleTimeFormat24h, + LocaleTimeFormat12h, +}; + +static void time_format_changed(VariableItem* item) { + // SystemSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, time_format_text[index]); + locale_set_time_format(time_format_value[index]); +} + +const char* const date_format_text[] = { + "D/M/Y", + "M/D/Y", + "Y/M/D", +}; + +const uint32_t date_format_value[] = { + LocaleDateFormatDMY, + LocaleDateFormatMDY, + LocaleDateFormatYMD, +}; + +static void date_format_changed(VariableItem* item) { + // SystemSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, date_format_text[index]); + locale_set_date_format(date_format_value[index]); +} + static uint32_t system_settings_exit(void* context) { UNUSED(context); return VIEW_NONE; @@ -91,6 +145,31 @@ SystemSettings* system_settings_alloc() { uint8_t value_index; app->var_item_list = variable_item_list_alloc(); + item = variable_item_list_add( + app->var_item_list, + "Units", + COUNT_OF(mesurement_units_text), + mesurement_units_changed, + app); + value_index = value_index_uint32( + locale_get_measurement_unit(), mesurement_units_value, COUNT_OF(mesurement_units_value)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, mesurement_units_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, "Time Format", COUNT_OF(time_format_text), time_format_changed, app); + value_index = value_index_uint32( + locale_get_time_format(), time_format_value, COUNT_OF(time_format_value)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, time_format_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, "Date Format", COUNT_OF(date_format_text), date_format_changed, app); + value_index = value_index_uint32( + locale_get_date_format(), date_format_value, COUNT_OF(date_format_value)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, date_format_text[value_index]); + item = variable_item_list_add( app->var_item_list, "Log Level", COUNT_OF(log_level_text), log_level_changed, app); value_index = value_index_uint32( diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index b3afb8427..d8db33a38 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.0,, +Version,+,11.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -29,6 +29,7 @@ Header,+,applications/services/gui/view_dispatcher.h,, Header,+,applications/services/gui/view_stack.h,, Header,+,applications/services/input/input.h,, Header,+,applications/services/loader/loader.h,, +Header,+,applications/services/locale/locale.h,, Header,+,applications/services/notification/notification.h,, Header,+,applications/services/notification/notification_messages.h,, Header,+,applications/services/power/power_service/power.h,, @@ -1265,6 +1266,9 @@ Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode, Function,+,furi_hal_rtc_get_datetime,void,FuriHalRtcDateTime* Function,+,furi_hal_rtc_get_fault_data,uint32_t, Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, +Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, +Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat, +Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits, Function,+,furi_hal_rtc_get_log_level,uint8_t, Function,+,furi_hal_rtc_get_pin_fails,uint32_t, Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister @@ -1278,6 +1282,9 @@ Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* Function,+,furi_hal_rtc_set_fault_data,void,uint32_t Function,+,furi_hal_rtc_set_flag,void,FuriHalRtcFlag Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode +Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat +Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat +Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits Function,+,furi_hal_rtc_set_log_level,void,uint8_t Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" @@ -1736,6 +1743,16 @@ Function,+,loader_update_menu,void, Function,+,loading_alloc,Loading*, Function,+,loading_free,void,Loading* Function,+,loading_get_view,View*,Loading* +Function,+,locale_celsius_to_fahrenheit,float,float +Function,+,locale_fahrenheit_to_celsius,float,float +Function,+,locale_format_date,void,"FuriString*, const FuriHalRtcDateTime*, const LocaleDateFormat, const char*" +Function,+,locale_format_time,void,"FuriString*, const FuriHalRtcDateTime*, const LocaleTimeFormat, const _Bool" +Function,+,locale_get_date_format,LocaleDateFormat, +Function,+,locale_get_measurement_unit,LocaleMeasurementUnits, +Function,+,locale_get_time_format,LocaleTimeFormat, +Function,+,locale_set_date_format,void,LocaleDateFormat +Function,+,locale_set_measurement_unit,void,LocaleMeasurementUnits +Function,+,locale_set_time_format,void,LocaleTimeFormat Function,-,localtime,tm*,const time_t* Function,-,localtime_r,tm*,"const time_t*, tm*" Function,-,log,double,double diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index c38cbfec5..e5fa8c767 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -29,12 +29,15 @@ typedef struct { uint8_t log_level : 4; uint8_t log_reserved : 4; uint8_t flags; - uint8_t boot_mode : 4; - uint8_t heap_track_mode : 2; - uint16_t reserved : 10; -} DeveloperReg; + FuriHalRtcBootMode boot_mode : 4; + FuriHalRtcHeapTrackMode heap_track_mode : 2; + FuriHalRtcLocaleUnits locale_units : 1; + FuriHalRtcLocaleTimeFormat locale_timeformat : 1; + FuriHalRtcLocaleDateFormat locale_dateformat : 2; + uint8_t reserved : 6; +} SystemReg; -_Static_assert(sizeof(DeveloperReg) == 4, "DeveloperReg size mismatch"); +_Static_assert(sizeof(SystemReg) == 4, "SystemReg size mismatch"); #define FURI_HAL_RTC_SECONDS_PER_MINUTE 60 #define FURI_HAL_RTC_SECONDS_PER_HOUR (FURI_HAL_RTC_SECONDS_PER_MINUTE * 60) @@ -172,7 +175,7 @@ void furi_hal_rtc_set_register(FuriHalRtcRegister reg, uint32_t value) { void furi_hal_rtc_set_log_level(uint8_t level) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; data->log_level = level; furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); furi_log_set_level(level); @@ -180,13 +183,13 @@ void furi_hal_rtc_set_log_level(uint8_t level) { uint8_t furi_hal_rtc_get_log_level() { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; return data->log_level; } void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; data->flags |= flag; furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); @@ -197,7 +200,7 @@ void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) { void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; data->flags &= ~flag; furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); @@ -208,34 +211,73 @@ void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag) { bool furi_hal_rtc_is_flag_set(FuriHalRtcFlag flag) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; return data->flags & flag; } void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; data->boot_mode = mode; furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); } FuriHalRtcBootMode furi_hal_rtc_get_boot_mode() { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; - return (FuriHalRtcBootMode)data->boot_mode; + SystemReg* data = (SystemReg*)&data_reg; + return data->boot_mode; } void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode) { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; + SystemReg* data = (SystemReg*)&data_reg; data->heap_track_mode = mode; furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); } FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode() { uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; - return (FuriHalRtcHeapTrackMode)data->heap_track_mode; + SystemReg* data = (SystemReg*)&data_reg; + return data->heap_track_mode; +} + +void furi_hal_rtc_set_locale_units(FuriHalRtcLocaleUnits value) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->locale_units = value; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->locale_units; +} + +void furi_hal_rtc_set_locale_timeformat(FuriHalRtcLocaleTimeFormat value) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->locale_timeformat = value; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->locale_timeformat; +} + +void furi_hal_rtc_set_locale_dateformat(FuriHalRtcLocaleDateFormat value) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->locale_dateformat = value; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->locale_dateformat; } void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime) { diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index 5ce122271..fe095e749 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -59,53 +59,194 @@ typedef enum { FuriHalRtcRegisterMAX, /**< Service value, do not use */ } FuriHalRtcRegister; +typedef enum { + FuriHalRtcLocaleUnitsMetric = 0, /**< Metric measurement units */ + FuriHalRtcLocaleUnitsImperial = 1, /**< Imperial measurement units */ +} FuriHalRtcLocaleUnits; + +typedef enum { + FuriHalRtcLocaleTimeFormat24h = 0, /**< 24-hour format */ + FuriHalRtcLocaleTimeFormat12h = 1, /**< 12-hour format */ +} FuriHalRtcLocaleTimeFormat; + +typedef enum { + FuriHalRtcLocaleDateFormatDMY = 0, /**< Day/Month/Year */ + FuriHalRtcLocaleDateFormatMDY = 1, /**< Month/Day/Year */ + FuriHalRtcLocaleDateFormatYMD = 2, /**< Year/Month/Day */ +} FuriHalRtcLocaleDateFormat; + /** Early initialization */ void furi_hal_rtc_init_early(); -/** Early deinitialization */ +/** Early de-initialization */ void furi_hal_rtc_deinit_early(); /** Initialize RTC subsystem */ void furi_hal_rtc_init(); +/** Get RTC register content + * + * @param[in] reg The register identifier + * + * @return content of the register + */ uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg); +/** Set register content + * + * @param[in] reg The register identifier + * @param[in] value The value to store into register + */ void furi_hal_rtc_set_register(FuriHalRtcRegister reg, uint32_t value); +/** Set Log Level value + * + * @param[in] level The level to store + */ void furi_hal_rtc_set_log_level(uint8_t level); +/** Get Log Level value + * + * @return The Log Level value + */ uint8_t furi_hal_rtc_get_log_level(); +/** Set RTC Flag + * + * @param[in] flag The flag to set + */ void furi_hal_rtc_set_flag(FuriHalRtcFlag flag); +/** Reset RTC Flag + * + * @param[in] flag The flag to reset + */ void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag); +/** Check if RTC Flag is set + * + * @param[in] flag The flag to check + * + * @return true if set + */ bool furi_hal_rtc_is_flag_set(FuriHalRtcFlag flag); +/** Set RTC boot mode + * + * @param[in] mode The mode to set + */ void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode); +/** Get RTC boot mode + * + * @return The RTC boot mode. + */ FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(); +/** Set Heap Track mode + * + * @param[in] mode The mode to set + */ void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode); +/** Get RTC Heap Track mode + * + * @return The RTC heap track mode. + */ FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(); +/** Set locale units + * + * @param[in] mode The RTC Locale Units + */ +void furi_hal_rtc_set_locale_units(FuriHalRtcLocaleUnits value); + +/** Get RTC Locale Units + * + * @return The RTC Locale Units. + */ +FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units(); + +/** Set RTC Locale Time Format + * + * @param[in] value The RTC Locale Time Format + */ +void furi_hal_rtc_set_locale_timeformat(FuriHalRtcLocaleTimeFormat value); + +/** Get RTC Locale Time Format + * + * @return The RTC Locale Time Format. + */ +FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat(); + +/** Set RTC Locale Date Format + * + * @param[in] value The RTC Locale Date Format + */ +void furi_hal_rtc_set_locale_dateformat(FuriHalRtcLocaleDateFormat value); + +/** Get RTC Locale Date Format + * + * @return The RTC Locale Date Format + */ +FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat(); + +/** Set RTC Date Time + * + * @param datetime The date time to set + */ void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime); +/** Get RTC Date Time + * + * @param datetime The datetime + */ void furi_hal_rtc_get_datetime(FuriHalRtcDateTime* datetime); +/** Validate Date Time + * + * @param datetime The datetime to validate + * + * @return { description_of_the_return_value } + */ bool furi_hal_rtc_validate_datetime(FuriHalRtcDateTime* datetime); +/** Set RTC Fault Data + * + * @param[in] value The value + */ void furi_hal_rtc_set_fault_data(uint32_t value); +/** Get RTC Fault Data + * + * @return RTC Fault Data value + */ uint32_t furi_hal_rtc_get_fault_data(); +/** Set Pin Fails count + * + * @param[in] value The Pin Fails count + */ void furi_hal_rtc_set_pin_fails(uint32_t value); +/** Get Pin Fails count + * + * @return Pin Fails Count + */ uint32_t furi_hal_rtc_get_pin_fails(); +/** Get UNIX Timestamp + * + * @return Unix Timestamp in seconds from UNIX epoch start + */ uint32_t furi_hal_rtc_get_timestamp(); +/** Convert DateTime to UNIX timestamp + * + * @param datetime The datetime + * + * @return UNIX Timestamp in seconds from UNIX epoch start + */ uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime); #ifdef __cplusplus From 94453d91003c38e7ebc946738056cbaefc414f2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 19 Dec 2022 20:57:44 +0900 Subject: [PATCH 20/21] [FL-3046] Notification: fix recursive speaker acquire #2147 --- applications/services/notification/notification_app.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index 4f6d42aa0..b6579f547 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -150,7 +150,7 @@ void notification_vibro_off() { } void notification_sound_on(float freq, float volume) { - if(furi_hal_speaker_acquire(30)) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { furi_hal_speaker_start(freq, volume); } } From 36e15a1352a6aa7068698647b71b7ccd56b8216f Mon Sep 17 00:00:00 2001 From: Konstantin Volkov <72250702+doomwastaken@users.noreply.github.com> Date: Mon, 19 Dec 2022 16:07:23 +0300 Subject: [PATCH 21/21] Doom/Unit_tests flashing proper firmware (#2133) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * doom: should fix all issues, needs review * fixed flash call and added port * increased timeout, full flash wasn't completing * turned serial back * added unit formatting and force flag for overwriting files * testing crash * fixed step names, added release flashing, removed unit_tests from updater tests * changed checkout method, added step validations * removed duplicated tag * fixed styling, stopped relying on shebang lines, removed debug output * moved format to the end, flash_usb_full copies resourses already * awaiting flipper after flashing and step status for file move Co-authored-by: Konstantin Volkov Co-authored-by: あく --- .github/workflows/amap_analyse.yml | 3 +- .github/workflows/build.yml | 5 +- .github/workflows/lint_c.yml | 4 +- .github/workflows/lint_python.yml | 6 ++- .github/workflows/merge_report.yml | 6 ++- .github/workflows/pvs_studio.yml | 5 +- .github/workflows/unit_tests.yml | 79 ++++++++++++++++++++++++------ scripts/testing/await_flipper.py | 2 +- 8 files changed, 85 insertions(+), 25 deletions(-) diff --git a/.github/workflows/amap_analyse.yml b/.github/workflows/amap_analyse.yml index 6231c5886..1340e4cde 100644 --- a/.github/workflows/amap_analyse.yml +++ b/.github/workflows/amap_analyse.yml @@ -11,6 +11,7 @@ on: env: TARGETS: f7 + FBT_TOOLCHAIN_PATH: /opt jobs: amap_analyse: @@ -78,7 +79,7 @@ jobs: - name: 'Upload report to DB' run: | - FBT_TOOLCHAIN_PATH=/opt source scripts/toolchain/fbtenv.sh + source scripts/toolchain/fbtenv.sh get_size() { SECTION="$1"; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 99c272e60..2de0e57c3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,7 @@ on: env: TARGETS: f7 DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /runner/_work jobs: main: @@ -55,7 +56,7 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - FBT_TOOLCHAIN_PATH=/runner/_work ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ + ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ copro_dist updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} done @@ -157,6 +158,6 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - FBT_TOOLCHAIN_PATH=/runner/_work ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ + ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ updater_package DEBUG=0 COMPACT=1 done diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml index 71ec24ff6..a6fd5127c 100644 --- a/.github/workflows/lint_c.yml +++ b/.github/workflows/lint_c.yml @@ -11,6 +11,8 @@ on: env: TARGETS: f7 + FBT_TOOLCHAIN_PATH: /runner/_work + SET_GH_OUTPUT: 1 jobs: lint_c_cpp: @@ -30,7 +32,7 @@ jobs: - name: 'Check code formatting' id: syntax_check - run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/runner/_work ./fbt lint + run: ./fbt lint - name: Report code formatting errors if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml index 44f233db8..66c36064c 100644 --- a/.github/workflows/lint_python.yml +++ b/.github/workflows/lint_python.yml @@ -9,6 +9,10 @@ on: - '*' pull_request: +env: + FBT_TOOLCHAIN_PATH: /runner/_work + SET_GH_OUTPUT: 1 + jobs: lint_python: runs-on: [self-hosted,FlipperZeroShell] @@ -26,4 +30,4 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: 'Check code formatting' - run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/runner/_work ./fbt lint_py + run: ./fbt lint_py diff --git a/.github/workflows/merge_report.yml b/.github/workflows/merge_report.yml index b6c089056..13fab0948 100644 --- a/.github/workflows/merge_report.yml +++ b/.github/workflows/merge_report.yml @@ -4,6 +4,10 @@ on: push: branches: - dev + +env: + FBT_TOOLCHAIN_PATH: /runner/_work + jobs: merge_report: runs-on: [self-hosted,FlipperZeroShell] @@ -33,7 +37,7 @@ jobs: - name: 'Check ticket and report' run: | - FBT_TOOLCHAIN_PATH=/runner/_work source scripts/toolchain/fbtenv.sh + source scripts/toolchain/fbtenv.sh python3 -m pip install slack_sdk python3 scripts/merge_report_qa.py \ ${{ secrets.QA_REPORT_SLACK_TOKEN }} \ diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index 5473f19f0..5bb04afcb 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -12,6 +12,7 @@ on: env: TARGETS: f7 DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /runner/_work jobs: analyse_c_cpp: @@ -49,11 +50,11 @@ jobs: - name: 'Generate compile_comands.json' run: | - FBT_TOOLCHAIN_PATH=/runner/_work ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking _fap_icons + ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking _fap_icons - name: 'Static code analysis' run: | - FBT_TOOLCHAIN_PATH=/runner/_work source scripts/toolchain/fbtenv.sh + source scripts/toolchain/fbtenv.sh pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} pvs-studio-analyzer analyze \ @.pvsoptions \ diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 308ec5929..361b647f7 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -6,6 +6,7 @@ on: env: TARGETS: f7 DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /opt jobs: run_units_on_test_bench: @@ -28,35 +29,81 @@ jobs: run: | echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT + - name: 'Flashing target firmware' + id: first_full_flash + run: | + ./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1 + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + + - name: 'Validating updater' + id: second_full_flash + if: success() + run: | + ./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1 + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + - name: 'Flash unit tests firmware' id: flashing + if: success() run: | - FBT_TOOLCHAIN_PATH=/opt ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 + ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 - name: 'Wait for flipper to finish updating' id: connect if: steps.flashing.outcome == 'success' run: | - . scripts/toolchain/fbtenv.sh - ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - - - name: 'Format flipper SD card' - id: format - if: steps.connect.outcome == 'success' - run: | - . scripts/toolchain/fbtenv.sh - ./scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - name: 'Copy assets and unit tests data to flipper' id: copy - if: steps.format.outcome == 'success' + if: steps.connect.outcome == 'success' run: | - . scripts/toolchain/fbtenv.sh - ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/resources /ext - ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/unit_tests /ext/unit_tests + source scripts/toolchain/fbtenv.sh + python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/unit_tests /ext/unit_tests - name: 'Run units and validate results' if: steps.copy.outcome == 'success' run: | - . scripts/toolchain/fbtenv.sh - ./scripts/testing/units.py ${{steps.device.outputs.flipper}} + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} + + - name: 'Get last release tag' + id: release_tag + if: success() + run: | + echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT + + - name: 'Decontaminate previous build leftovers' + if: success() + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout latest release' + uses: actions/checkout@v3 + if: success() + with: + fetch-depth: 0 + ref: ${{ steps.release_tag.outputs.tag }} + + - name: 'Flash last release' + if: success() + run: | + ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 + + - name: 'Wait for flipper to finish updating' + if: success() + run: | + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + + - name: 'Format flipper SD card' + id: format + if: success() + run: | + source scripts/toolchain/fbtenv.sh + python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext diff --git a/scripts/testing/await_flipper.py b/scripts/testing/await_flipper.py index 1f0d16194..efae6765d 100755 --- a/scripts/testing/await_flipper.py +++ b/scripts/testing/await_flipper.py @@ -24,7 +24,7 @@ def flp_serial_by_name(flp_name): return "" -UPDATE_TIMEOUT = 30 +UPDATE_TIMEOUT = 60 def main():